static int wl_cfgvendor_gscan_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); dhd_pno_gscan_capabilities_t *reply = NULL; uint32 reply_len = 0; reply = dhd_dev_pno_get_gscan(bcmcfg_to_prmry_ndev(cfg), DHD_PNO_GET_CAPABILITIES, NULL, &reply_len); if (!reply) { WL_ERR(("Could not get capabilities\n")); err = -EINVAL; return err; } err = rtw_cfgvendor_send_cmd_reply(wiphy, bcmcfg_to_prmry_ndev(cfg), reply, reply_len); if (unlikely(err)) WL_ERR(("Vendor Command reply failed ret:%d \n", err)); kfree(reply); return err; }
s32 dhd_config_dongle(struct bcm_cfg80211 *cfg) { #ifndef DHD_SDALIGN #define DHD_SDALIGN 32 #endif struct net_device *ndev; s32 err = 0; WL_TRACE(("In\n")); if (dhd_dongle_up) { WL_ERR(("Dongle is already up\n")); return err; } ndev = bcmcfg_to_prmry_ndev(cfg); err = wl_dongle_up(ndev); if (unlikely(err)) { WL_ERR(("wl_dongle_up failed\n")); goto default_conf_out; } dhd_dongle_up = true; default_conf_out: return err; }
static int wl_cfgvendor_get_feature_set(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); int reply; reply = dhd_dev_get_feature_set(bcmcfg_to_prmry_ndev(cfg)); err = wl_cfgvendor_send_cmd_reply(wiphy, bcmcfg_to_prmry_ndev(cfg), &reply, sizeof(int)); if (unlikely(err)) WL_ERR(("Vendor Command reply failed ret:%d \n", err)); return err; }
s32 dhd_cfg80211_down(struct bcm_cfg80211 *cfg) { struct net_device *ndev; s32 err = 0; WL_TRACE(("In\n")); if (!dhd_dongle_up) { WL_ERR(("Dongle is already down\n")); return err; } ndev = bcmcfg_to_prmry_ndev(cfg); wl_dongle_down(ndev); dhd_dongle_up = FALSE; return 0; }
static int wl_cfgvendor_gscan_get_channel_list(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0, type, band; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); uint16 *reply = NULL; uint32 reply_len = 0, num_channels, mem_needed; struct sk_buff *skb; type = nla_type(data); if (type == GSCAN_ATTRIBUTE_BAND) { band = nla_get_u32(data); } else { return -1; } reply = dhd_dev_pno_get_gscan(bcmcfg_to_prmry_ndev(cfg), DHD_PNO_GET_CHANNEL_LIST, &band, &reply_len); if (!reply) { WL_ERR(("Could not get channel list\n")); err = -EINVAL; return err; } num_channels = reply_len/ sizeof(uint32); mem_needed = reply_len + VENDOR_REPLY_OVERHEAD + (ATTRIBUTE_U32_LEN * 2); /* Alloc the SKB for vendor_event */ skb = rtw_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed); if (unlikely(!skb)) { WL_ERR(("skb alloc failed")); err = -ENOMEM; goto exit; } nla_put_u32(skb, GSCAN_ATTRIBUTE_NUM_CHANNELS, num_channels); nla_put(skb, GSCAN_ATTRIBUTE_CHANNEL_LIST, reply_len, reply); err = rtw_cfg80211_vendor_cmd_reply(skb); if (unlikely(err)) WL_ERR(("Vendor Command reply failed ret:%d \n", err)); exit: kfree(reply); return err; }
static int wl_cfgvendor_set_nodfs_flag(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); int type; u32 nodfs; type = nla_type(data); if (type == ANDR_WIFI_ATTRIBUTE_NODFS_SET) { nodfs = nla_get_u32(data); err = dhd_dev_set_nodfs(bcmcfg_to_prmry_ndev(cfg), nodfs); } else { err = -1; } return err; }
static int wl_cfgvendor_get_feature_set_matrix(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); struct sk_buff *skb; int *reply; int num, mem_needed, i; reply = dhd_dev_get_feature_set_matrix(bcmcfg_to_prmry_ndev(cfg), &num); if (!reply) { WL_ERR(("Could not get feature list matrix\n")); err = -EINVAL; return err; } mem_needed = VENDOR_REPLY_OVERHEAD + (ATTRIBUTE_U32_LEN * num) + ATTRIBUTE_U32_LEN; /* Alloc the SKB for vendor_event */ skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed); if (unlikely(!skb)) { WL_ERR(("skb alloc failed")); err = -ENOMEM; goto exit; } nla_put_u32(skb, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, num); for (i = 0; i < num; i++) { nla_put_u32(skb, ANDR_WIFI_ATTRIBUTE_FEATURE_SET, reply[i]); } err = cfg80211_vendor_cmd_reply(skb); if (unlikely(err)) WL_ERR(("Vendor Command reply failed ret:%d \n", err)); exit: kfree(reply); return err; }
static int wl_cfgvendor_set_pno_mac_oui(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); int type; uint8 pno_random_mac_oui[DOT11_OUI_LEN]; type = nla_type(data); if (type == ANDR_WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI) { memcpy(pno_random_mac_oui, nla_data(data), DOT11_OUI_LEN); err = dhd_dev_pno_set_mac_oui(bcmcfg_to_prmry_ndev(cfg), pno_random_mac_oui); if (unlikely(err)) WL_ERR(("Bad OUI, could not set:%d \n", err)); } else { err = -1; } return err; }
s32 wl_cfgnan_notify_nan_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *event, void *data) { s32 ret = BCME_OK; u16 data_len; u32 event_num; s32 event_type; nan_event_hdr_t nan_hdr; wl_nan_tlv_data_t tlv_data; u8 *buf = NULL; u32 buf_len; u8 *ptr; u16 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL; if (!event || !data) { WL_ERR((" event data is NULL \n")); return -EINVAL; } event_type = ntoh32(event->event_type); event_num = ntoh32(event->reason); data_len = ntoh32(event->datalen); memset(&nan_hdr, 0, sizeof(nan_event_hdr_t)); nan_hdr.event_subtype = event_num; WL_DBG((" nan event: type: %d num: %d len: %d \n", event_type, event_num, data_len)); if (INVALID_NAN_EVENT(event_num)) { WL_ERR((" unsupported event, num: %d \n", event_num)); return -EINVAL; } if (g_nan_debug) { WL_DBG((" event name: %s \n", nan_event_name[event_num])); WL_DBG((" event data: \n")); prhex(NULL, data, data_len); } /* unpack the tlvs */ memset(&tlv_data, 0, sizeof(wl_nan_tlv_data_t)); bcm_unpack_xtlv_buf(&tlv_data, data, data_len, wl_cfgnan_set_vars_cbfn); /* * send as preformatted hex string * * EVENT_NAN <event_type> <tlv_hex_string> */ buf_len = NAN_IOCTL_BUF_SIZE; buf = ptr = kzalloc(buf_len, kflags); if (!buf) { WL_ERR((" memory allocation failed \n")); ret = -ENOMEM; goto fail; } switch (event_num) { case WL_NAN_EVENT_START: case WL_NAN_EVENT_JOIN: case WL_NAN_EVENT_STOP: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " CLUS_ID_PREFIX MACF, nan_event_str[event_num], ETHER_TO_MACF(tlv_data.nstatus.cid)); break; case WL_NAN_EVENT_ROLE: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s "ROLE_PREFIX "%d " CLUS_ID_PREFIX MACF, nan_event_str[event_num], tlv_data.nstatus.role, ETHER_TO_MACF(tlv_data.nstatus.cid)); break; case WL_NAN_EVENT_DISCOVERY_RESULT: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " PUB_ID_PREFIX"%d " SUB_ID_PREFIX"%d " MAC_ADDR_PREFIX MACF, nan_event_str[event_num], tlv_data.pub_id, tlv_data.sub_id, ETHER_TO_MACF(tlv_data.mac_addr)); if (tlv_data.svc_info.data && tlv_data.svc_info.dlen) { WL_DBG((" service info present \n")); if ((strlen(ptr) + tlv_data.svc_info.dlen) >= buf_len) { WL_ERR((" service info length = %d\n", tlv_data.svc_info.dlen)); WL_ERR((" insufficent buffer to copy service info \n")); ret = -EOVERFLOW; goto fail; } ptr += sprintf(ptr, " %s", SVC_INFO_PREFIX); ptr += bcm_format_hex(ptr, tlv_data.svc_info.data, tlv_data.svc_info.dlen); } else { WL_DBG((" service info not present \n")); } if (tlv_data.vend_info.data && tlv_data.vend_info.dlen) { struct ether_addr *ea; u8 *data = tlv_data.vend_info.data; uint32 bitmap; u16 dlen = tlv_data.vend_info.dlen; chanspec_t chanspec; uint8 mapcontrol; uint8 proto; WL_DBG((" vendor info present \n")); if ((*data != NAN_ATTR_VENDOR_SPECIFIC) || (dlen < NAN_VENDOR_HDR_SIZE)) { WL_ERR((" error in vendor info attribute \n")); ret = -EINVAL; goto fail; } else { WL_DBG((" vendor info not present \n")); } if (*(data + 6) == NAN_VENDOR_TYPE_RTT) { data += NAN_VENDOR_HDR_SIZE; ea = (struct ether_addr *)data; data += ETHER_ADDR_LEN; mapcontrol = *data++; proto = *data++; bitmap = *(uint32 *)data; data += 4; chanspec = *(chanspec_t *)data; ptr += sprintf(ptr, " "BITMAP_PREFIX"0x%x "CHAN_PREFIX"%d/%s", bitmap, wf_chspec_ctlchan(chanspec), wf_chspec_to_bw_str(chanspec)); WL_DBG((" bitmap: 0x%x channel: %d bandwidth: %s \n", bitmap, wf_chspec_ctlchan(chanspec), wf_chspec_to_bw_str(chanspec))); } } break; case WL_NAN_EVENT_REPLIED: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " PUB_ID_PREFIX"%d " MAC_ADDR_PREFIX MACF, nan_event_str[event_num], tlv_data.pub_id, ETHER_TO_MACF(tlv_data.mac_addr)); break; case WL_NAN_EVENT_TERMINATED: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " PUB_ID_PREFIX"%d ", nan_event_str[event_num], tlv_data.pub_id); break; case WL_NAN_EVENT_RECEIVE: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " PUB_ID_PREFIX"%d " MAC_ADDR_PREFIX MACF, nan_event_str[event_num], tlv_data.pub_id, ETHER_TO_MACF(tlv_data.mac_addr)); if (tlv_data.svc_info.data && tlv_data.svc_info.dlen) { WL_DBG((" service info present \n")); if ((strlen(ptr) + tlv_data.svc_info.dlen) >= buf_len) { WL_ERR((" service info length = %d\n", tlv_data.svc_info.dlen)); WL_ERR((" insufficent buffer to copy service info \n")); ret = -EOVERFLOW; goto fail; } ptr += sprintf(ptr, " %s", SVC_INFO_PREFIX); ptr += bcm_format_hex(ptr, tlv_data.svc_info.data, tlv_data.svc_info.dlen); } else { WL_DBG((" service info not present \n")); } break; case WL_NAN_EVENT_SCAN_COMPLETE: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " CLUS_ID_PREFIX MACF, nan_event_str[event_num], ETHER_TO_MACF(tlv_data.nstatus.cid)); break; case WL_NAN_EVENT_STATUS_CHG: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " CLUS_ID_PREFIX MACF, nan_event_str[event_num], ETHER_TO_MACF(tlv_data.nstatus.cid)); break; case WL_NAN_EVENT_MERGE: ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " CLUS_ID_PREFIX MACF, nan_event_str[event_num], ETHER_TO_MACF(tlv_data.nstatus.cid)); break; default: WL_ERR((" unknown event \n")); break; } #ifdef WL_GENL /* send the preformatted string to the upper layer as event */ WL_DBG((" formatted string for userspace: %s, len: %zu \n", buf, strlen(buf))); wl_genl_send_msg(bcmcfg_to_prmry_ndev(cfg), 0, buf, strlen(buf), 0, 0); #endif /* WL_GENL */ fail: if (buf) { kfree(buf); } if (tlv_data.svc_info.data) { kfree(tlv_data.svc_info.data); tlv_data.svc_info.data = NULL; tlv_data.svc_info.dlen = 0; } if (tlv_data.vend_info.data) { kfree(tlv_data.vend_info.data); tlv_data.vend_info.data = NULL; tlv_data.vend_info.dlen = 0; } return ret; }
s32 wl_cfgnan_notify_proxd_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *event, void *data) { s32 ret = BCME_OK; wl_nan_ranging_event_data_t *rdata; s32 status; u16 data_len; s32 event_type; s32 event_num; u8 *buf = NULL; u32 buf_len; u8 *ptr; u16 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL; s32 i; if (!event || !data) { WL_ERR((" event data is NULL \n")); return -EINVAL; } status = ntoh32(event->reason); event_type = ntoh32(event->event_type); event_num = ntoh32(event->reason); data_len = ntoh32(event->datalen); WL_DBG((" proxd event: type: %d num: %d len: %d \n", event_type, event_num, data_len)); if (INVALID_PROXD_EVENT(event_num)) { WL_ERR((" unsupported event, num: %d \n", event_num)); return -EINVAL; } if (g_nan_debug) { WL_DBG((" event name: WLC_E_PROXD_NAN_EVENT \n")); WL_DBG((" event data: \n")); prhex(NULL, data, data_len); } if (data_len < sizeof(wl_nan_ranging_event_data_t)) { WL_ERR((" wrong data len \n")); return -EINVAL; } rdata = (wl_nan_ranging_event_data_t *)data; WL_DBG((" proxd event: count:%d success_count:%d mode:%d \n", rdata->count, rdata->success_count, rdata->mode)); if (g_nan_debug) { prhex(" event data: ", data, data_len); } buf_len = NAN_IOCTL_BUF_SIZE; buf = kzalloc(buf_len, kflags); if (!buf) { WL_ERR((" memory allocation failed \n")); return -ENOMEM; } for (i = 0; i < rdata->count; i++) { if (&rdata->rr[i] == NULL) { ret = -EINVAL; goto fail; } ptr = buf; WL_DBG((" ranging data for mac:"MACDBG" \n", MAC2STRDBG(rdata->rr[i].ea.octet))); ptr += sprintf(buf, SUPP_EVENT_PREFIX"%s " MAC_ADDR_PREFIX MACF " "STATUS_PREFIX"%s", EVENT_RTT_STATUS_STR, ETHER_TO_MACF(rdata->rr[i].ea), (rdata->rr[i].status == 1) ? "success" : "fail"); if (rdata->rr[i].status == 1) { /* add tsf and distance only if status is success */ ptr += sprintf(ptr, " "TIMESTAMP_PREFIX"0x%x " DISTANCE_PREFIX"%d.%04d", rdata->rr[i].timestamp, rdata->rr[i].distance >> 4, ((rdata->rr[i].distance & 0x0f) * 625)); } #ifdef WL_GENL /* send the preformatted string to the upper layer as event */ WL_DBG((" formatted string for userspace: %s, len: %zu \n", buf, strlen(buf))); wl_genl_send_msg(bcmcfg_to_prmry_ndev(cfg), 0, buf, strlen(buf), 0, 0); #endif /* WL_GENL */ }
static int wl_cfgvendor_gscan_get_batch_results(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { int err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); gscan_results_cache_t *results, *iter; uint32 reply_len, complete = 0, num_results_iter; int32 mem_needed; wifi_gscan_result_t *ptr; uint16 num_scan_ids, num_results; struct sk_buff *skb; struct nlattr *scan_hdr; dhd_dev_wait_batch_results_complete(bcmcfg_to_prmry_ndev(cfg)); dhd_dev_pno_lock_access_batch_results(bcmcfg_to_prmry_ndev(cfg)); results = dhd_dev_pno_get_gscan(bcmcfg_to_prmry_ndev(cfg), DHD_PNO_GET_BATCH_RESULTS, NULL, &reply_len); if (!results) { WL_ERR(("No results to send %d\n", err)); err = rtw_cfgvendor_send_cmd_reply(wiphy, bcmcfg_to_prmry_ndev(cfg), results, 0); if (unlikely(err)) WL_ERR(("Vendor Command reply failed ret:%d \n", err)); dhd_dev_pno_unlock_access_batch_results(bcmcfg_to_prmry_ndev(cfg)); return err; } num_scan_ids = reply_len & 0xFFFF; num_results = (reply_len & 0xFFFF0000) >> 16; mem_needed = (num_results * sizeof(wifi_gscan_result_t)) + (num_scan_ids * GSCAN_BATCH_RESULT_HDR_LEN) + VENDOR_REPLY_OVERHEAD + SCAN_RESULTS_COMPLETE_FLAG_LEN; if (mem_needed > (int32)NLMSG_DEFAULT_SIZE) { mem_needed = (int32)NLMSG_DEFAULT_SIZE; complete = 0; } else { complete = 1; } WL_TRACE(("complete %d mem_needed %d max_mem %d\n", complete, mem_needed, (int)NLMSG_DEFAULT_SIZE)); /* Alloc the SKB for vendor_event */ skb = rtw_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed); if (unlikely(!skb)) { WL_ERR(("skb alloc failed")); dhd_dev_pno_unlock_access_batch_results(bcmcfg_to_prmry_ndev(cfg)); return -ENOMEM; } iter = results; nla_put_u32(skb, GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE, complete); mem_needed = mem_needed - (SCAN_RESULTS_COMPLETE_FLAG_LEN + VENDOR_REPLY_OVERHEAD); while (iter && ((mem_needed - GSCAN_BATCH_RESULT_HDR_LEN) > 0)) { scan_hdr = nla_nest_start(skb, GSCAN_ATTRIBUTE_SCAN_RESULTS); nla_put_u32(skb, GSCAN_ATTRIBUTE_SCAN_ID, iter->scan_id); nla_put_u8(skb, GSCAN_ATTRIBUTE_SCAN_FLAGS, iter->flag); num_results_iter = (mem_needed - GSCAN_BATCH_RESULT_HDR_LEN)/sizeof(wifi_gscan_result_t); if ((iter->tot_count - iter->tot_consumed) < num_results_iter) num_results_iter = iter->tot_count - iter->tot_consumed; nla_put_u32(skb, GSCAN_ATTRIBUTE_NUM_OF_RESULTS, num_results_iter); if (num_results_iter) { ptr = &iter->results[iter->tot_consumed]; iter->tot_consumed += num_results_iter; nla_put(skb, GSCAN_ATTRIBUTE_SCAN_RESULTS, num_results_iter * sizeof(wifi_gscan_result_t), ptr); } nla_nest_end(skb, scan_hdr); mem_needed -= GSCAN_BATCH_RESULT_HDR_LEN + (num_results_iter * sizeof(wifi_gscan_result_t)); iter = iter->next; } dhd_dev_gscan_batch_cache_cleanup(bcmcfg_to_prmry_ndev(cfg)); dhd_dev_pno_unlock_access_batch_results(bcmcfg_to_prmry_ndev(cfg)); return rtw_cfg80211_vendor_cmd_reply(skb); }