| /* |
| * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: wlan_hdd_ext_scan.c |
| * |
| * WLAN Host Device Driver EXT SCAN feature implementation |
| * |
| */ |
| |
| #ifdef FEATURE_WLAN_EXTSCAN |
| |
| #include "wlan_hdd_ext_scan.h" |
| #include "wlan_hdd_regulatory.h" |
| #include "cds_utils.h" |
| #include "cds_sched.h" |
| #include <qca_vendor.h> |
| |
| #define EXTSCAN_PARAM_MAX QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| |
| /* amount of time to wait for a synchronous request/response operation */ |
| #define WLAN_WAIT_TIME_EXTSCAN 1000 |
| |
| /** |
| * struct hdd_ext_scan_context - hdd ext scan context |
| * @request_id: userspace-assigned ID associated with the request |
| * @response_event: Ext scan wait event |
| * @response_status: Status returned by FW in response to a request |
| * @ignore_cached_results: Flag to ignore cached results or not |
| * @context_lock: Spinlock to serialize all context accesses |
| * @capability_response: Ext scan capability response data from target |
| * @buckets_scanned: bitmask of buckets scanned in extscan cycle |
| */ |
| struct hdd_ext_scan_context { |
| uint32_t request_id; |
| int response_status; |
| bool ignore_cached_results; |
| struct completion response_event; |
| spinlock_t context_lock; |
| struct ext_scan_capabilities_response capability_response; |
| uint32_t buckets_scanned; |
| }; |
| static struct hdd_ext_scan_context ext_scan_context; |
| |
| static const struct nla_policy wlan_hdd_extscan_config_policy |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CLASS] = {.type = NLA_U8}, |
| |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND] = {.type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS] = { |
| .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS] = { |
| .type = NLA_U8}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH] = { |
| .type = NLA_U8}, |
| |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID] = { |
| .type = NLA_UNSPEC, |
| .len = QDF_MAC_ADDR_SIZE}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW] = { |
| .type = NLA_S32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH] = { |
| .type = NLA_S32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_CHANNEL] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_SSID] = { |
| .type = NLA_BINARY, |
| .len = IEEE80211_MAX_SSID_LEN + 1 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE] = { |
| .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_NUM_SSID] = { |
| .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_BAND] = { |
| .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW] = { |
| .type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH] = { |
| .type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS] = { |
| .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE] = { |
| .type = NLA_U32}, |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_pno_config_policy[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID] = { |
| .type = NLA_BINARY, |
| .len = IEEE80211_MAX_SSID_LEN + 1 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS] = { |
| .type = NLA_U8 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT] = { |
| .type = NLA_U8 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID] = { |
| .type = NLA_U32 |
| }, |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_extscan_results_policy[QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD] = { |
| .type = NLA_U16}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY] = { |
| .type = NLA_U16}, |
| }; |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_capabilities_rsp() - response from target |
| * @hdd_ctx: Pointer to hdd context |
| * @data: Pointer to ext scan capabilities response from fw |
| * |
| * Return: None |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_get_capabilities_rsp(struct hdd_context *hdd_ctx, |
| struct ext_scan_capabilities_response *data) |
| { |
| struct hdd_ext_scan_context *context; |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| context = &ext_scan_context; |
| |
| spin_lock(&context->context_lock); |
| /* validate response received from target*/ |
| if (context->request_id != data->requestId) { |
| spin_unlock(&context->context_lock); |
| hdd_err("Target response id did not match. request_id: %d response_id: %d", |
| context->request_id, data->requestId); |
| return; |
| } |
| |
| context->capability_response = *data; |
| complete(&context->response_event); |
| spin_unlock(&context->context_lock); |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by hdd_extscan_nl_fill_bss() |
| */ |
| #define PARAM_TIME_STAMP \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP |
| #define PARAM_SSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID |
| #define PARAM_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID |
| #define PARAM_CHANNEL \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL |
| #define PARAM_RSSI \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI |
| #define PARAM_RTT \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT |
| #define PARAM_RTT_SD \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD |
| #define PARAM_BEACON_PERIOD \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD |
| #define PARAM_CAPABILITY \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY |
| #define PARAM_IE_LENGTH \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH |
| #define PARAM_IE_DATA \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA |
| |
| /** hdd_extscan_nl_fill_bss() - extscan nl fill bss |
| * @skb: socket buffer |
| * @ap: bss information |
| * @idx: nesting index |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_extscan_nl_fill_bss(struct sk_buff *skb, tSirWifiScanResult *ap, |
| int idx) |
| { |
| struct nlattr *nla_ap; |
| |
| nla_ap = nla_nest_start(skb, idx); |
| if (!nla_ap) |
| return -EINVAL; |
| |
| if (hdd_wlan_nla_put_u64(skb, PARAM_TIME_STAMP, ap->ts) || |
| nla_put(skb, PARAM_SSID, sizeof(ap->ssid), ap->ssid) || |
| nla_put(skb, PARAM_BSSID, sizeof(ap->bssid), ap->bssid.bytes) || |
| nla_put_u32(skb, PARAM_CHANNEL, ap->channel) || |
| nla_put_s32(skb, PARAM_RSSI, ap->rssi) || |
| nla_put_u32(skb, PARAM_RTT, ap->rtt) || |
| nla_put_u32(skb, PARAM_RTT_SD, ap->rtt_sd) || |
| nla_put_u16(skb, PARAM_BEACON_PERIOD, ap->beaconPeriod) || |
| nla_put_u16(skb, PARAM_CAPABILITY, ap->capability) || |
| nla_put_u16(skb, PARAM_IE_LENGTH, ap->ieLength)) { |
| hdd_err("put fail"); |
| return -EINVAL; |
| } |
| |
| if (ap->ieLength) |
| if (nla_put(skb, PARAM_IE_DATA, ap->ieLength, ap->ieData)) { |
| hdd_err("put fail"); |
| return -EINVAL; |
| } |
| |
| nla_nest_end(skb, nla_ap); |
| |
| return 0; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by hdd_extscan_nl_fill_bss() |
| */ |
| #undef PARAM_TIME_STAMP |
| #undef PARAM_SSID |
| #undef PARAM_BSSID |
| #undef PARAM_CHANNEL |
| #undef PARAM_RSSI |
| #undef PARAM_RTT |
| #undef PARAM_RTT_SD |
| #undef PARAM_BEACON_PERIOD |
| #undef PARAM_CAPABILITY |
| #undef PARAM_IE_LENGTH |
| #undef PARAM_IE_DATA |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_cached_results_ind() - get cached results |
| * @hdd_ctx: hdd global context |
| * @data: cached results |
| * |
| * This function reads the cached results %data, populated the NL |
| * attributes and sends the NL event to the upper layer. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_cached_results_ind(struct hdd_context *hdd_ctx, |
| struct extscan_cached_scan_results *data) |
| { |
| struct sk_buff *skb = NULL; |
| struct hdd_ext_scan_context *context; |
| struct extscan_cached_scan_result *result; |
| tSirWifiScanResult *ap; |
| uint32_t i, j, nl_buf_len; |
| bool ignore_cached_results = false; |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| ignore_cached_results = context->ignore_cached_results; |
| spin_unlock(&context->context_lock); |
| |
| if (ignore_cached_results) { |
| hdd_err("Ignore the cached results received after timeout"); |
| return; |
| } |
| |
| #define EXTSCAN_CACHED_NEST_HDRLEN NLA_HDRLEN |
| #define EXTSCAN_CACHED_NL_FIXED_TLV \ |
| ((sizeof(data->request_id) + NLA_HDRLEN) + \ |
| (sizeof(data->num_scan_ids) + NLA_HDRLEN) + \ |
| (sizeof(data->more_data) + NLA_HDRLEN)) |
| #define EXTSCAN_CACHED_NL_SCAN_ID_TLV \ |
| ((sizeof(result->scan_id) + NLA_HDRLEN) + \ |
| (sizeof(result->flags) + NLA_HDRLEN) + \ |
| (sizeof(result->num_results) + NLA_HDRLEN))+ \ |
| (sizeof(result->buckets_scanned) + NLA_HDRLEN) |
| #define EXTSCAN_CACHED_NL_SCAN_RESULTS_TLV \ |
| ((sizeof(ap->ts) + NLA_HDRLEN) + \ |
| (sizeof(ap->ssid) + NLA_HDRLEN) + \ |
| (sizeof(ap->bssid) + NLA_HDRLEN) + \ |
| (sizeof(ap->channel) + NLA_HDRLEN) + \ |
| (sizeof(ap->rssi) + NLA_HDRLEN) + \ |
| (sizeof(ap->rtt) + NLA_HDRLEN) + \ |
| (sizeof(ap->rtt_sd) + NLA_HDRLEN) + \ |
| (sizeof(ap->beaconPeriod) + NLA_HDRLEN) + \ |
| (sizeof(ap->capability) + NLA_HDRLEN) + \ |
| (sizeof(ap->ieLength) + NLA_HDRLEN)) |
| #define EXTSCAN_CACHED_NL_SCAN_RESULTS_IE_DATA_TLV \ |
| (ap->ieLength + NLA_HDRLEN) |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += EXTSCAN_CACHED_NL_FIXED_TLV; |
| if (data->num_scan_ids) { |
| nl_buf_len += sizeof(result->scan_id) + NLA_HDRLEN; |
| nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; |
| result = &data->result[0]; |
| for (i = 0; i < data->num_scan_ids; i++) { |
| nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; |
| nl_buf_len += EXTSCAN_CACHED_NL_SCAN_ID_TLV; |
| nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; |
| |
| ap = &result->ap[0]; |
| for (j = 0; j < result->num_results; j++) { |
| nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; |
| nl_buf_len += |
| EXTSCAN_CACHED_NL_SCAN_RESULTS_TLV; |
| if (ap->ieLength) |
| nl_buf_len += |
| EXTSCAN_CACHED_NL_SCAN_RESULTS_IE_DATA_TLV; |
| ap++; |
| } |
| result++; |
| } |
| } |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| goto fail; |
| } |
| hdd_debug("Req Id %u Num_scan_ids %u More Data %u", |
| data->request_id, data->num_scan_ids, data->more_data); |
| |
| result = &data->result[0]; |
| for (i = 0; i < data->num_scan_ids; i++) { |
| hdd_debug("[i=%d] scan_id %u flags %u num_results %u buckets scanned %u", |
| i, result->scan_id, result->flags, result->num_results, |
| result->buckets_scanned); |
| |
| ap = &result->ap[0]; |
| for (j = 0; j < result->num_results; j++) { |
| /* |
| * Firmware returns timestamp from ext scan start till |
| * BSSID was cached (in micro seconds). Add this with |
| * time gap between system boot up to ext scan start |
| * to derive the time since boot when the |
| * BSSID was cached. |
| */ |
| ap->ts += hdd_ctx->ext_scan_start_since_boot; |
| hdd_debug("Timestamp %llu " |
| "Ssid: %s " |
| "Bssid (" MAC_ADDRESS_STR ") " |
| "Channel %u " |
| "Rssi %d " |
| "RTT %u " |
| "RTT_SD %u " |
| "Beacon Period %u " |
| "Capability 0x%x " |
| "Ie length %d", |
| ap->ts, |
| ap->ssid, |
| MAC_ADDR_ARRAY(ap->bssid.bytes), |
| ap->channel, |
| ap->rssi, |
| ap->rtt, |
| ap->rtt_sd, |
| ap->beaconPeriod, |
| ap->capability, |
| ap->ieLength); |
| ap++; |
| } |
| result++; |
| } |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->request_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, |
| data->num_scan_ids) || |
| nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, |
| data->more_data)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| if (data->num_scan_ids) { |
| struct nlattr *nla_results; |
| |
| result = &data->result[0]; |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID, |
| result->scan_id)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| nla_results = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_LIST); |
| if (!nla_results) |
| goto fail; |
| |
| for (i = 0; i < data->num_scan_ids; i++) { |
| struct nlattr *nla_result; |
| struct nlattr *nla_aps; |
| |
| nla_result = nla_nest_start(skb, i); |
| if (!nla_result) |
| goto fail; |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID, |
| result->scan_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_FLAGS, |
| result->flags) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED, |
| result->buckets_scanned) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, |
| result->num_results)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| nla_aps = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); |
| if (!nla_aps) |
| goto fail; |
| |
| ap = &result->ap[0]; |
| for (j = 0; j < result->num_results; j++) { |
| if (hdd_extscan_nl_fill_bss(skb, ap, j)) |
| goto fail; |
| |
| ap++; |
| } |
| nla_nest_end(skb, nla_aps); |
| nla_nest_end(skb, nla_result); |
| result++; |
| } |
| nla_nest_end(skb, nla_results); |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| |
| if (!data->more_data) { |
| spin_lock(&context->context_lock); |
| context->response_status = 0; |
| complete(&context->response_event); |
| spin_unlock(&context->context_lock); |
| } |
| return; |
| |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| |
| spin_lock(&context->context_lock); |
| context->response_status = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_hotlist_match_ind() - hot list match ind |
| * @hdd_ctx: Pointer to hdd context |
| * @data: Pointer to ext scan result event |
| * |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_hotlist_match_ind(struct hdd_context *hdd_ctx, |
| struct extscan_hotlist_match *data) |
| { |
| struct sk_buff *skb = NULL; |
| uint32_t i, index; |
| int flags = cds_get_gfp_flags(); |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| if (data->ap_found) |
| index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX; |
| else |
| index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX; |
| |
| skb = cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| index, flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| hdd_debug("Req Id: %u Num_APs: %u MoreData: %u ap_found: %u", |
| data->requestId, data->numOfAps, data->moreData, |
| data->ap_found); |
| |
| for (i = 0; i < data->numOfAps; i++) { |
| data->ap[i].ts = qdf_get_monotonic_boottime(); |
| |
| hdd_debug("[i=%d] Timestamp %llu " |
| "Ssid: %s " |
| "Bssid (" MAC_ADDRESS_STR ") " |
| "Channel %u " |
| "Rssi %d " |
| "RTT %u " |
| "RTT_SD %u", |
| i, |
| data->ap[i].ts, |
| data->ap[i].ssid, |
| MAC_ADDR_ARRAY(data->ap[i].bssid.bytes), |
| data->ap[i].channel, |
| data->ap[i].rssi, |
| data->ap[i].rtt, data->ap[i].rtt_sd); |
| } |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->requestId) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, |
| data->numOfAps)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| if (data->numOfAps) { |
| struct nlattr *aps; |
| |
| aps = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); |
| if (!aps) |
| goto fail; |
| |
| for (i = 0; i < data->numOfAps; i++) { |
| struct nlattr *ap; |
| |
| ap = nla_nest_start(skb, i); |
| if (!ap) |
| goto fail; |
| |
| if (hdd_wlan_nla_put_u64(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, |
| data->ap[i].ts) || |
| nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID, |
| sizeof(data->ap[i].ssid), |
| data->ap[i].ssid) || |
| nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID, |
| sizeof(data->ap[i].bssid), |
| data->ap[i].bssid.bytes) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL, |
| data->ap[i].channel) || |
| nla_put_s32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI, |
| data->ap[i].rssi) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT, |
| data->ap[i].rtt) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD, |
| data->ap[i].rtt_sd)) |
| goto fail; |
| |
| nla_nest_end(skb, ap); |
| } |
| nla_nest_end(skb, aps); |
| |
| if (nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, |
| data->moreData)) |
| goto fail; |
| } |
| |
| cfg80211_vendor_event(skb, flags); |
| hdd_exit(); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind() - |
| * significant wifi change results indication |
| * @hdd_ctx: Pointer to hdd context |
| * @data: Pointer to signif wifi change event |
| * |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind( |
| struct hdd_context *hdd_ctx, |
| tpSirWifiSignificantChangeEvent data) |
| { |
| struct sk_buff *skb = NULL; |
| tSirWifiSignificantChange *ap_info; |
| int32_t *rssi; |
| uint32_t i, j; |
| int flags = cds_get_gfp_flags(); |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| hdd_debug("Req Id %u Num results %u More Data %u", |
| data->requestId, data->numResults, data->moreData); |
| |
| ap_info = &data->ap[0]; |
| for (i = 0; i < data->numResults; i++) { |
| hdd_debug("[i=%d] " |
| "Bssid (" MAC_ADDRESS_STR ") " |
| "Channel %u " |
| "numOfRssi %d", |
| i, |
| MAC_ADDR_ARRAY(ap_info->bssid.bytes), |
| ap_info->channel, ap_info->numOfRssi); |
| rssi = &(ap_info)->rssi[0]; |
| for (j = 0; j < ap_info->numOfRssi; j++) |
| hdd_debug("Rssi %d", *rssi++); |
| |
| ap_info = (tSirWifiSignificantChange *)((char *)ap_info + |
| ap_info->numOfRssi * sizeof(*rssi) + |
| sizeof(*ap_info)); |
| } |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->requestId) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, |
| data->numResults)) { |
| hdd_err("put fail"); |
| goto fail; |
| } |
| |
| if (data->numResults) { |
| struct nlattr *aps; |
| |
| aps = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); |
| if (!aps) |
| goto fail; |
| |
| ap_info = &data->ap[0]; |
| for (i = 0; i < data->numResults; i++) { |
| struct nlattr *ap; |
| |
| ap = nla_nest_start(skb, i); |
| if (!ap) |
| goto fail; |
| |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID, |
| QDF_MAC_ADDR_SIZE, ap_info->bssid.bytes) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL, |
| ap_info->channel) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI, |
| ap_info->numOfRssi) || |
| nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST, |
| sizeof(s32) * ap_info->numOfRssi, |
| &(ap_info)->rssi[0])) |
| goto fail; |
| |
| nla_nest_end(skb, ap); |
| |
| ap_info = (tSirWifiSignificantChange *)((char *)ap_info |
| + ap_info->numOfRssi * sizeof(*rssi) + |
| sizeof(*ap_info)); |
| } |
| nla_nest_end(skb, aps); |
| |
| if (nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, |
| data->moreData)) |
| goto fail; |
| } |
| |
| cfg80211_vendor_event(skb, flags); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| return; |
| |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_full_scan_result_event() - full scan result event |
| * @hdd_ctx: Pointer to hdd context |
| * @data: Pointer to full scan result event |
| * |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_full_scan_result_event(struct hdd_context *hdd_ctx, |
| tpSirWifiFullScanResultEvent |
| data) |
| { |
| struct sk_buff *skb; |
| struct timespec ts; |
| struct hdd_ext_scan_context *context; |
| |
| int flags = cds_get_gfp_flags(); |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| if ((sizeof(*data) + data->ap.ieLength) >= EXTSCAN_EVENT_BUF_SIZE) { |
| hdd_err("Frame exceeded NL size limitation, drop it!!"); |
| return; |
| } |
| skb = cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| data->ap.channel = cds_chan_to_freq(data->ap.channel); |
| |
| /* |
| * Android does not want the time stamp from the frame. |
| * Instead it wants a monotonic increasing value since boot |
| */ |
| get_monotonic_boottime(&ts); |
| data->ap.ts = ((u64)ts.tv_sec * 1000000) + (ts.tv_nsec / 1000); |
| |
| hdd_debug("Req Id %u More Data %u", data->requestId, |
| data->moreData); |
| hdd_debug("AP Info: Timestamp %llu Ssid: %s " |
| "Bssid (" MAC_ADDRESS_STR ") " |
| "Channel %u " |
| "Rssi %d " |
| "RTT %u " |
| "RTT_SD %u " |
| "Bcn Period %d " |
| "Capability 0x%X " |
| "IE Length %d", |
| data->ap.ts, |
| data->ap.ssid, |
| MAC_ADDR_ARRAY(data->ap.bssid.bytes), |
| data->ap.channel, |
| data->ap.rssi, |
| data->ap.rtt, |
| data->ap.rtt_sd, |
| data->ap.beaconPeriod, |
| data->ap.capability, data->ap.ieLength); |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->requestId) || |
| hdd_wlan_nla_put_u64(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, |
| data->ap.ts) || |
| nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID, |
| sizeof(data->ap.ssid), |
| data->ap.ssid) || |
| nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID, |
| sizeof(data->ap.bssid), |
| data->ap.bssid.bytes) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL, |
| data->ap.channel) || |
| nla_put_s32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI, |
| data->ap.rssi) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT, |
| data->ap.rtt) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD, |
| data->ap.rtt_sd) || |
| nla_put_u16(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD, |
| data->ap.beaconPeriod) || |
| nla_put_u16(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY, |
| data->ap.capability) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH, |
| data->ap.ieLength) || |
| nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, |
| data->moreData)) { |
| hdd_err("nla put fail"); |
| goto nla_put_failure; |
| } |
| |
| if (data->ap.ieLength) { |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA, |
| data->ap.ieLength, data->ap.ieData)) |
| goto nla_put_failure; |
| } |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED, |
| context->buckets_scanned)) { |
| spin_unlock(&context->context_lock); |
| hdd_debug("Failed to include buckets_scanned"); |
| goto nla_put_failure; |
| } |
| spin_unlock(&context->context_lock); |
| |
| cfg80211_vendor_event(skb, flags); |
| return; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_scan_res_available_event() - scan result event |
| * @hdd_ctx: Pointer to hdd context |
| * @data: Pointer to scan results available indication param |
| * |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_scan_res_available_event( |
| struct hdd_context *hdd_ctx, |
| tpSirExtScanResultsAvailableIndParams data) |
| { |
| struct sk_buff *skb; |
| int flags = cds_get_gfp_flags(); |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| hdd_debug("Req Id %u Num results %u", |
| data->requestId, data->numResultsAvailable); |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->requestId) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, |
| data->numResultsAvailable)) { |
| hdd_err("nla put fail"); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_event(skb, flags); |
| hdd_exit(); |
| return; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_scan_progress_event() - scan progress event |
| * @hdd_ctx: Pointer to hdd context |
| * @data: Pointer to scan event indication param |
| * |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_scan_progress_event(struct hdd_context *hdd_ctx, |
| tpSirExtScanOnScanEventIndParams |
| data) |
| { |
| struct sk_buff *skb; |
| int flags = cds_get_gfp_flags(); |
| struct hdd_ext_scan_context *context; |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc( |
| hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| hdd_debug("Request Id: %u Scan event type: %u Scan event status: %u buckets scanned: %u", |
| data->requestId, data->scanEventType, data->status, |
| data->buckets_scanned); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| if (data->scanEventType == WIFI_EXTSCAN_CYCLE_COMPLETED_EVENT) { |
| context->buckets_scanned = 0; |
| data->scanEventType = WIFI_EXTSCAN_RESULTS_AVAILABLE; |
| spin_unlock(&context->context_lock); |
| } else if (data->scanEventType == WIFI_EXTSCAN_CYCLE_STARTED_EVENT) { |
| context->buckets_scanned = data->buckets_scanned; |
| /* No need to report to user space */ |
| spin_unlock(&context->context_lock); |
| goto nla_put_failure; |
| } else { |
| spin_unlock(&context->context_lock); |
| } |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->requestId) || |
| nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_EVENT_TYPE, |
| data->scanEventType)) { |
| hdd_err("nla put fail"); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_event(skb, flags); |
| return; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_epno_match_found() - pno match found |
| * @hdd_ctx: HDD context |
| * @data: matched network data |
| * |
| * This function reads the matched network data and fills NL vendor attributes |
| * and send it to upper layer. |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_epno_match_found(struct hdd_context *hdd_ctx, |
| struct pno_match_found *data) |
| { |
| struct sk_buff *skb; |
| uint32_t len, i; |
| int flags = cds_get_gfp_flags(); |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| /* |
| * If the number of match found APs including IE data exceeds NL 4K size |
| * limitation, drop that beacon/probe rsp frame. |
| */ |
| len = sizeof(*data) + |
| (data->num_results + sizeof(tSirWifiScanResult)); |
| for (i = 0; i < data->num_results; i++) |
| len += data->ap[i].ieLength; |
| |
| if (len >= EXTSCAN_EVENT_BUF_SIZE) { |
| hdd_err("Frame exceeded NL size limitation, drop it!"); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| hdd_debug("Req Id %u More Data %u num_results %d", |
| data->request_id, data->more_data, data->num_results); |
| for (i = 0; i < data->num_results; i++) { |
| data->ap[i].channel = cds_chan_to_freq(data->ap[i].channel); |
| hdd_debug("AP Info: Timestamp %llu) Ssid: %s " |
| "Bssid (" MAC_ADDRESS_STR ") " |
| "Channel %u " |
| "Rssi %d " |
| "RTT %u " |
| "RTT_SD %u " |
| "Bcn Period %d " |
| "Capability 0x%X " |
| "IE Length %d", |
| data->ap[i].ts, |
| data->ap[i].ssid, |
| MAC_ADDR_ARRAY(data->ap[i].bssid.bytes), |
| data->ap[i].channel, |
| data->ap[i].rssi, |
| data->ap[i].rtt, |
| data->ap[i].rtt_sd, |
| data->ap[i].beaconPeriod, |
| data->ap[i].capability, |
| data->ap[i].ieLength); |
| } |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->request_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, |
| data->num_results) || |
| nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, |
| data->more_data)) { |
| hdd_err("nla put fail"); |
| goto fail; |
| } |
| |
| if (data->num_results) { |
| struct nlattr *nla_aps; |
| |
| nla_aps = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); |
| if (!nla_aps) |
| goto fail; |
| |
| for (i = 0; i < data->num_results; i++) { |
| if (hdd_extscan_nl_fill_bss(skb, &data->ap[i], i)) |
| goto fail; |
| } |
| nla_nest_end(skb, nla_aps); |
| } |
| |
| cfg80211_vendor_event(skb, flags); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_passpoint_match_found() - passpoint match found |
| * @hddctx: HDD context |
| * @data: matched network data |
| * |
| * This function reads the match network %data and fill in the skb with |
| * NL attributes and send up the NL event |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_passpoint_match_found(void *ctx, |
| struct wifi_passpoint_match *data) |
| { |
| struct hdd_context *hdd_ctx = ctx; |
| struct sk_buff *skb = NULL; |
| uint32_t len, i, num_matches = 1, more_data = 0; |
| struct nlattr *nla_aps, *nla_bss; |
| int flags = cds_get_gfp_flags(); |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| if (!data) { |
| hdd_err("data is null"); |
| return; |
| } |
| |
| len = sizeof(*data) + data->ap.ieLength + data->anqp_len; |
| if (len >= EXTSCAN_EVENT_BUF_SIZE) { |
| hdd_err("Result exceeded NL size limitation, drop it"); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND_INDEX, |
| flags); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_event_alloc failed"); |
| return; |
| } |
| |
| hdd_debug("Req Id %u Id %u ANQP length %u num_matches %u", |
| data->request_id, data->id, data->anqp_len, num_matches); |
| for (i = 0; i < num_matches; i++) { |
| hdd_debug("AP Info: Timestamp %llu Ssid: %s " |
| "Bssid (" MAC_ADDRESS_STR ") " |
| "Channel %u " |
| "Rssi %d " |
| "RTT %u " |
| "RTT_SD %u " |
| "Bcn Period %d " |
| "Capability 0x%X " |
| "IE Length %d", |
| data->ap.ts, |
| data->ap.ssid, |
| MAC_ADDR_ARRAY(data->ap.bssid.bytes), |
| data->ap.channel, |
| data->ap.rssi, |
| data->ap.rtt, |
| data->ap.rtt_sd, |
| data->ap.beaconPeriod, |
| data->ap.capability, |
| data->ap.ieLength); |
| } |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, |
| data->request_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES, |
| num_matches) || |
| nla_put_u8(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, |
| more_data)) { |
| hdd_err("nla put fail"); |
| goto fail; |
| } |
| |
| nla_aps = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST); |
| if (!nla_aps) |
| goto fail; |
| |
| for (i = 0; i < num_matches; i++) { |
| struct nlattr *nla_ap; |
| |
| nla_ap = nla_nest_start(skb, i); |
| if (!nla_ap) |
| goto fail; |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID, |
| data->id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN, |
| data->anqp_len)) { |
| goto fail; |
| } |
| |
| if (data->anqp_len) |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP, |
| data->anqp_len, data->anqp)) |
| goto fail; |
| |
| nla_bss = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); |
| if (!nla_bss) |
| goto fail; |
| |
| if (hdd_extscan_nl_fill_bss(skb, &data->ap, 0)) |
| goto fail; |
| |
| nla_nest_end(skb, nla_bss); |
| nla_nest_end(skb, nla_ap); |
| } |
| nla_nest_end(skb, nla_aps); |
| |
| cfg80211_vendor_event(skb, flags); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_generic_rsp() - |
| * Handle a generic ExtScan Response message |
| * @ctx: HDD context registered with SME |
| * @response: The ExtScan response from firmware |
| * |
| * This function will handle a generic ExtScan response message from |
| * firmware and will communicate the result to the userspace thread |
| * that is waiting for the response. |
| * |
| * Return: none |
| */ |
| static void |
| wlan_hdd_cfg80211_extscan_generic_rsp(struct hdd_context *hdd_ctx, |
| struct sir_extscan_generic_response *response) |
| { |
| struct hdd_ext_scan_context *context; |
| |
| hdd_enter(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx) || !response) { |
| hdd_err("HDD context is not valid or response(%pK) is null", |
| response); |
| return; |
| } |
| |
| hdd_debug("request %u status %u", |
| response->request_id, response->status); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| if (context->request_id == response->request_id) { |
| context->response_status = response->status ? -EINVAL : 0; |
| complete(&context->response_event); |
| } |
| spin_unlock(&context->context_lock); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_callback() - ext scan callback |
| * @hdd_handle: Opaque handle to hdd context |
| * @event_id: Event identifier |
| * @msg: Pointer to message |
| * |
| * Return: none |
| */ |
| void wlan_hdd_cfg80211_extscan_callback(hdd_handle_t hdd_handle, |
| const uint16_t event_id, void *msg) |
| { |
| struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| hdd_debug("Rcvd Event %d", event_id); |
| |
| switch (event_id) { |
| case eSIR_EXTSCAN_CACHED_RESULTS_RSP: |
| /* There is no need to send this response to upper layer |
| * Just log the message |
| */ |
| hdd_debug("Rcvd eSIR_EXTSCAN_CACHED_RESULTS_RSP"); |
| break; |
| |
| case eSIR_EXTSCAN_GET_CAPABILITIES_IND: |
| wlan_hdd_cfg80211_extscan_get_capabilities_rsp(hdd_ctx, msg); |
| break; |
| |
| case eSIR_EXTSCAN_HOTLIST_MATCH_IND: |
| wlan_hdd_cfg80211_extscan_hotlist_match_ind(hdd_ctx, msg); |
| break; |
| |
| case eSIR_EXTSCAN_SIGNIFICANT_WIFI_CHANGE_RESULTS_IND: |
| wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind(hdd_ctx, |
| msg); |
| break; |
| |
| case eSIR_EXTSCAN_CACHED_RESULTS_IND: |
| wlan_hdd_cfg80211_extscan_cached_results_ind(hdd_ctx, msg); |
| break; |
| |
| case eSIR_EXTSCAN_SCAN_RES_AVAILABLE_IND: |
| wlan_hdd_cfg80211_extscan_scan_res_available_event(hdd_ctx, |
| msg); |
| break; |
| |
| case eSIR_EXTSCAN_FULL_SCAN_RESULT_IND: |
| wlan_hdd_cfg80211_extscan_full_scan_result_event(hdd_ctx, msg); |
| break; |
| |
| case eSIR_EPNO_NETWORK_FOUND_IND: |
| wlan_hdd_cfg80211_extscan_epno_match_found(hdd_ctx, msg); |
| break; |
| |
| case eSIR_EXTSCAN_SCAN_PROGRESS_EVENT_IND: |
| wlan_hdd_cfg80211_extscan_scan_progress_event(hdd_ctx, msg); |
| break; |
| |
| case eSIR_PASSPOINT_NETWORK_FOUND_IND: |
| wlan_hdd_cfg80211_passpoint_match_found(hdd_ctx, msg); |
| break; |
| |
| case eSIR_EXTSCAN_START_RSP: |
| case eSIR_EXTSCAN_STOP_RSP: |
| case eSIR_EXTSCAN_SET_BSSID_HOTLIST_RSP: |
| case eSIR_EXTSCAN_RESET_BSSID_HOTLIST_RSP: |
| case eSIR_EXTSCAN_SET_SIGNIFICANT_WIFI_CHANGE_RSP: |
| case eSIR_EXTSCAN_RESET_SIGNIFICANT_WIFI_CHANGE_RSP: |
| case eSIR_EXTSCAN_SET_SSID_HOTLIST_RSP: |
| case eSIR_EXTSCAN_RESET_SSID_HOTLIST_RSP: |
| wlan_hdd_cfg80211_extscan_generic_rsp(hdd_ctx, msg); |
| break; |
| |
| default: |
| hdd_err("Unknown event type: %u", event_id); |
| break; |
| } |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_send_ext_scan_capability() |
| */ |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define PARAM_STATUS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_STATUS |
| #define MAX_SCAN_CACHE_SIZE \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE |
| #define MAX_SCAN_BUCKETS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS |
| #define MAX_AP_CACHE_PER_SCAN \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN |
| #define MAX_RSSI_SAMPLE_SIZE \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE |
| #define MAX_SCAN_RPT_THRHOLD \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD |
| #define MAX_HOTLIST_BSSIDS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS |
| #define MAX_SIGNIFICANT_WIFI_CHANGE_APS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS |
| #define MAX_BSSID_HISTORY_ENTRIES \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES |
| #define MAX_HOTLIST_SSIDS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS |
| #define MAX_NUM_EPNO_NETS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS |
| #define MAX_NUM_EPNO_NETS_BY_SSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID |
| #define MAX_NUM_WHITELISTED_SSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID |
| #define MAX_NUM_BLACKLISTED_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID |
| /** |
| * wlan_hdd_send_ext_scan_capability - send ext scan capability to user space |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_send_ext_scan_capability(struct hdd_context *hdd_ctx) |
| { |
| int ret; |
| struct sk_buff *skb; |
| struct ext_scan_capabilities_response *data; |
| uint32_t nl_buf_len; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| data = &(ext_scan_context.capability_response); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += (sizeof(data->requestId) + NLA_HDRLEN) + |
| (sizeof(data->status) + NLA_HDRLEN) + |
| (sizeof(data->max_scan_cache_size) + NLA_HDRLEN) + |
| (sizeof(data->max_scan_buckets) + NLA_HDRLEN) + |
| (sizeof(data->max_ap_cache_per_scan) + NLA_HDRLEN) + |
| (sizeof(data->max_rssi_sample_size) + NLA_HDRLEN) + |
| (sizeof(data->max_scan_reporting_threshold) + NLA_HDRLEN) + |
| (sizeof(data->max_hotlist_bssids) + NLA_HDRLEN) + |
| (sizeof(data->max_significant_wifi_change_aps) + NLA_HDRLEN) + |
| (sizeof(data->max_bssid_history_entries) + NLA_HDRLEN) + |
| (sizeof(data->max_hotlist_ssids) + NLA_HDRLEN) + |
| (sizeof(data->max_number_epno_networks) + NLA_HDRLEN) + |
| (sizeof(data->max_number_epno_networks_by_ssid) + NLA_HDRLEN) + |
| (sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN) + |
| (sizeof(data->max_number_of_black_listed_bssid) + NLA_HDRLEN); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| |
| if (!skb) { |
| hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); |
| return -ENOMEM; |
| } |
| |
| |
| hdd_debug("Req Id %u", data->requestId); |
| hdd_debug("Status %u", data->status); |
| hdd_debug("Scan cache size %u", |
| data->max_scan_cache_size); |
| hdd_debug("Scan buckets %u", data->max_scan_buckets); |
| hdd_debug("Max AP per scan %u", |
| data->max_ap_cache_per_scan); |
| hdd_debug("max_rssi_sample_size %u", |
| data->max_rssi_sample_size); |
| hdd_debug("max_scan_reporting_threshold %u", |
| data->max_scan_reporting_threshold); |
| hdd_debug("max_hotlist_bssids %u", |
| data->max_hotlist_bssids); |
| hdd_debug("max_significant_wifi_change_aps %u", |
| data->max_significant_wifi_change_aps); |
| hdd_debug("max_bssid_history_entries %u", |
| data->max_bssid_history_entries); |
| hdd_debug("max_hotlist_ssids %u", data->max_hotlist_ssids); |
| hdd_debug("max_number_epno_networks %u", |
| data->max_number_epno_networks); |
| hdd_debug("max_number_epno_networks_by_ssid %u", |
| data->max_number_epno_networks_by_ssid); |
| hdd_debug("max_number_of_white_listed_ssid %u", |
| data->max_number_of_white_listed_ssid); |
| hdd_debug("max_number_of_black_listed_bssid (%u)", |
| data->max_number_of_black_listed_bssid); |
| |
| if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) || |
| nla_put_u32(skb, PARAM_STATUS, data->status) || |
| nla_put_u32(skb, MAX_SCAN_CACHE_SIZE, data->max_scan_cache_size) || |
| nla_put_u32(skb, MAX_SCAN_BUCKETS, data->max_scan_buckets) || |
| nla_put_u32(skb, MAX_AP_CACHE_PER_SCAN, |
| data->max_ap_cache_per_scan) || |
| nla_put_u32(skb, MAX_RSSI_SAMPLE_SIZE, |
| data->max_rssi_sample_size) || |
| nla_put_u32(skb, MAX_SCAN_RPT_THRHOLD, |
| data->max_scan_reporting_threshold) || |
| nla_put_u32(skb, MAX_HOTLIST_BSSIDS, data->max_hotlist_bssids) || |
| nla_put_u32(skb, MAX_SIGNIFICANT_WIFI_CHANGE_APS, |
| data->max_significant_wifi_change_aps) || |
| nla_put_u32(skb, MAX_BSSID_HISTORY_ENTRIES, |
| data->max_bssid_history_entries) || |
| nla_put_u32(skb, MAX_HOTLIST_SSIDS, data->max_hotlist_ssids) || |
| nla_put_u32(skb, MAX_NUM_EPNO_NETS, |
| data->max_number_epno_networks) || |
| nla_put_u32(skb, MAX_NUM_EPNO_NETS_BY_SSID, |
| data->max_number_epno_networks_by_ssid) || |
| nla_put_u32(skb, MAX_NUM_WHITELISTED_SSID, |
| data->max_number_of_white_listed_ssid) || |
| nla_put_u32(skb, MAX_NUM_BLACKLISTED_BSSID, |
| data->max_number_of_black_listed_bssid)) { |
| hdd_err("nla put fail"); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_send_ext_scan_capability() |
| */ |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_STATUS |
| #undef MAX_SCAN_CACHE_SIZE |
| #undef MAX_SCAN_BUCKETS |
| #undef MAX_AP_CACHE_PER_SCAN |
| #undef MAX_RSSI_SAMPLE_SIZE |
| #undef MAX_SCAN_RPT_THRHOLD |
| #undef MAX_HOTLIST_BSSIDS |
| #undef MAX_SIGNIFICANT_WIFI_CHANGE_APS |
| #undef MAX_BSSID_HISTORY_ENTRIES |
| #undef MAX_HOTLIST_SSIDS |
| #undef MAX_NUM_EPNO_NETS |
| #undef MAX_NUM_EPNO_NETS_BY_SSID |
| #undef MAX_NUM_WHITELISTED_SSID |
| #undef MAX_NUM_BLACKLISTED_BSSID |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int __wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| unsigned long rc; |
| struct hdd_ext_scan_context *context; |
| tpSirGetExtScanCapabilitiesReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + |
| 1]; |
| QDF_STATUS status; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { |
| hdd_err("Driver Modules are closed"); |
| return -EINVAL; |
| } |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| pReqMsg = qdf_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = |
| nla_get_u32(tb |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = adapter->session_id; |
| hdd_debug("Req Id %d Session Id %d", |
| pReqMsg->requestId, pReqMsg->sessionId); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| context->request_id = pReqMsg->requestId; |
| INIT_COMPLETION(context->response_event); |
| spin_unlock(&context->context_lock); |
| |
| status = sme_ext_scan_get_capabilities(hdd_ctx->mac_handle, pReqMsg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_ext_scan_get_capabilities failed(err=%d)", |
| status); |
| goto fail; |
| } |
| |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| if (!rc) { |
| hdd_err("Target response timed out"); |
| return -ETIMEDOUT; |
| } |
| |
| ret = wlan_hdd_send_ext_scan_capability(hdd_ctx); |
| if (ret) |
| hdd_err("Failed to send ext scan capability to user space"); |
| hdd_exit(); |
| return ret; |
| fail: |
| qdf_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_get_capabilities(wiphy, wdev, data, |
| data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_get_cached_results() |
| */ |
| #define PARAM_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define PARAM_FLUSH \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH |
| /** |
| * __wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * This function parses the incoming NL vendor command data attributes and |
| * invokes the SME Api and blocks on a completion variable. |
| * Each WMI event with cached scan results data chunk results in |
| * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each |
| * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. |
| * |
| * If timeout happens before receiving all of the data, this function sets |
| * a context variable @ignore_cached_results to %true, all of the next data |
| * chunks are checked against this variable and dropped. |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int __wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, |
| struct wireless_dev |
| *wdev, const void *data, |
| int data_len) |
| { |
| tpSirExtScanGetCachedResultsReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + |
| 1]; |
| struct hdd_ext_scan_context *context; |
| QDF_STATUS status; |
| int retval = 0; |
| unsigned long rc; |
| |
| /* ENTER_DEV() intentionally not used in a frequently invoked API */ |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| pReqMsg = qdf_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = adapter->session_id; |
| |
| /* Parse and fetch flush parameter */ |
| if (!tb[PARAM_FLUSH]) { |
| hdd_err("attr flush failed"); |
| goto fail; |
| } |
| pReqMsg->flush = nla_get_u8(tb[PARAM_FLUSH]); |
| hdd_debug("Req Id: %u Session Id: %d Flush: %d", |
| pReqMsg->requestId, pReqMsg->sessionId, pReqMsg->flush); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| context->request_id = pReqMsg->requestId; |
| context->ignore_cached_results = false; |
| INIT_COMPLETION(context->response_event); |
| spin_unlock(&context->context_lock); |
| |
| status = sme_get_cached_results(hdd_ctx->mac_handle, pReqMsg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_get_cached_results failed(err=%d)", status); |
| goto fail; |
| } |
| |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| if (!rc) { |
| hdd_err("Target response timed out"); |
| retval = -ETIMEDOUT; |
| spin_lock(&context->context_lock); |
| context->ignore_cached_results = true; |
| spin_unlock(&context->context_lock); |
| } else { |
| spin_lock(&context->context_lock); |
| retval = context->response_status; |
| spin_unlock(&context->context_lock); |
| } |
| return retval; |
| |
| fail: |
| qdf_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_get_cached_results() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_FLUSH |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results |
| * @wiphy: wiphy pointer |
| * @wdev: pointer to struct wireless_dev |
| * @data: pointer to incoming NL vendor data |
| * @data_len: length of @data |
| * |
| * This function parses the incoming NL vendor command data attributes and |
| * invokes the SME Api and blocks on a completion variable. |
| * Each WMI event with cached scan results data chunk results in |
| * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each |
| * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. |
| * |
| * If timeout happens before receiving all of the data, this function sets |
| * a context variable @ignore_cached_results to %true, all of the next data |
| * chunks are checked against this variable and dropped. |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_get_cached_results(wiphy, wdev, data, |
| data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_parse_ap_rssi_threshold() - parse AP RSSI threshold parameters |
| * @attr: netlink attribute containing the AP RSSI threshold parameters |
| * @ap: destination buffer for the parsed parameters |
| * |
| * This function parses the BSSID, low RSSI and high RSSI values from |
| * the @attr netlink attribute, storing the parsed values in @ap. |
| * |
| * Return: 0 if @attr is parsed and all required attributes are |
| * present, otherwise a negative errno. |
| */ |
| static int hdd_parse_ap_rssi_threshold(struct nlattr *attr, |
| struct ap_threshold_params *ap) |
| { |
| struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; |
| int id; |
| |
| if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, |
| nla_data(attr), nla_len(attr), |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch MAC address */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID; |
| if (!tb[id]) { |
| hdd_err("attr mac address failed"); |
| return -EINVAL; |
| } |
| nla_memcpy(ap->bssid.bytes, tb[id], QDF_MAC_ADDR_SIZE); |
| hdd_debug("BSSID: " MAC_ADDRESS_STR, MAC_ADDR_ARRAY(ap->bssid.bytes)); |
| |
| /* Parse and fetch low RSSI */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW; |
| if (!tb[id]) { |
| hdd_err("attr low RSSI failed"); |
| return -EINVAL; |
| } |
| ap->low = nla_get_s32(tb[id]); |
| hdd_debug("RSSI low %d", ap->low); |
| |
| /* Parse and fetch high RSSI */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH; |
| if (!tb[id]) { |
| hdd_err("attr high RSSI failed"); |
| return -EINVAL; |
| } |
| ap->high = nla_get_s32(tb[id]); |
| hdd_debug("RSSI High %d", ap->high); |
| |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set bssid hot list |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct extscan_bssid_hotlist_set_params *params; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; |
| struct nlattr *apth; |
| struct hdd_ext_scan_context *context; |
| QDF_STATUS status; |
| uint8_t i; |
| int id, rem, retval; |
| unsigned long rc; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| params = qdf_mem_malloc(sizeof(*params)); |
| if (!params) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| |
| /* assume the worst until proven otherwise */ |
| retval = -EINVAL; |
| |
| /* Parse and fetch request Id */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; |
| if (!tb[id]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| |
| params->request_id = nla_get_u32(tb[id]); |
| hdd_debug("Req Id %d", params->request_id); |
| |
| /* Parse and fetch number of APs */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP; |
| if (!tb[id]) { |
| hdd_err("attr number of AP failed"); |
| goto fail; |
| } |
| |
| params->num_ap = nla_get_u32(tb[id]); |
| if (params->num_ap > WMI_WLAN_EXTSCAN_MAX_HOTLIST_APS) { |
| hdd_err("Number of AP: %u exceeds max: %u", |
| params->num_ap, WMI_WLAN_EXTSCAN_MAX_HOTLIST_APS); |
| goto fail; |
| } |
| params->vdev_id = adapter->session_id; |
| hdd_debug("Number of AP %d vdev Id %d", |
| params->num_ap, params->vdev_id); |
| |
| /* Parse and fetch lost ap sample size */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE; |
| if (!tb[id]) { |
| hdd_err("attr lost ap sample size failed"); |
| goto fail; |
| } |
| |
| params->lost_ap_sample_size = nla_get_u32(tb[id]); |
| hdd_debug("Lost ap sample size %d", |
| params->lost_ap_sample_size); |
| |
| /* Parse the AP Threshold array */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM; |
| if (!tb[id]) { |
| hdd_err("attr ap threshold failed"); |
| goto fail; |
| } |
| |
| i = 0; |
| nla_for_each_nested(apth, tb[id], rem) { |
| if (i == params->num_ap) { |
| hdd_warn("Ignoring excess AP"); |
| break; |
| } |
| |
| retval = hdd_parse_ap_rssi_threshold(apth, ¶ms->ap[i]); |
| if (retval) |
| goto fail; |
| |
| i++; |
| } |
| |
| if (i < params->num_ap) { |
| hdd_warn("Number of AP %u less than expected %u", |
| i, params->num_ap); |
| params->num_ap = i; |
| } |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = params->request_id; |
| spin_unlock(&context->context_lock); |
| |
| status = sme_set_bss_hotlist(hdd_ctx->mac_handle, params); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_set_bss_hotlist failed(err=%d)", status); |
| retval = qdf_status_to_os_return(status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout |
| (&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hdd_err("sme_set_bss_hotlist timed out"); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&context->context_lock); |
| if (context->request_id == params->request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| hdd_exit(); |
| |
| fail: |
| qdf_mem_free(params); |
| return retval; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set ext scan bssid hotlist |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(wiphy, wdev, data, |
| data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct extscan_set_sig_changereq_params *params; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; |
| struct nlattr *apth; |
| struct hdd_ext_scan_context *context; |
| QDF_STATUS status; |
| uint8_t i; |
| int id, rem, retval; |
| unsigned long rc; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| params = qdf_mem_malloc(sizeof(*params)); |
| if (!params) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| |
| /* assume the worst until proven otherwise */ |
| retval = -EINVAL; |
| |
| /* Parse and fetch request Id */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; |
| if (!tb[id]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| |
| params->request_id = nla_get_u32(tb[id]); |
| hdd_debug("Req Id %d", params->request_id); |
| |
| /* Parse and fetch RSSI sample size */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE; |
| if (!tb[id]) { |
| hdd_err("attr RSSI sample size failed"); |
| goto fail; |
| } |
| params->rssi_sample_size = nla_get_u32(tb[id]); |
| hdd_debug("RSSI sample size %u", params->rssi_sample_size); |
| |
| /* Parse and fetch lost AP sample size */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE; |
| if (!tb[id]) { |
| hdd_err("attr lost AP sample size failed"); |
| goto fail; |
| } |
| params->lostap_sample_size = nla_get_u32(tb[id]); |
| hdd_debug("Lost AP sample size %u", params->lostap_sample_size); |
| |
| /* Parse and fetch AP min breaching */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING; |
| if (!tb[id]) { |
| hdd_err("attr AP min breaching"); |
| goto fail; |
| } |
| params->min_breaching = nla_get_u32(tb[id]); |
| hdd_debug("AP min breaching %u", params->min_breaching); |
| |
| /* Parse and fetch number of APs */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP; |
| if (!tb[id]) { |
| hdd_err("attr number of AP failed"); |
| goto fail; |
| } |
| params->num_ap = nla_get_u32(tb[id]); |
| if (params->num_ap > WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS) { |
| hdd_err("Number of AP %u exceeds max %u", |
| params->num_ap, |
| WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS); |
| goto fail; |
| } |
| |
| params->vdev_id = adapter->session_id; |
| hdd_debug("Number of AP %d Vdev Id %d", |
| params->num_ap, params->vdev_id); |
| |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM; |
| if (!tb[id]) { |
| hdd_err("attr ap threshold failed"); |
| goto fail; |
| } |
| i = 0; |
| nla_for_each_nested(apth, tb[id], rem) { |
| |
| if (i == params->num_ap) { |
| hdd_warn("Ignoring excess AP"); |
| break; |
| } |
| |
| retval = hdd_parse_ap_rssi_threshold(apth, ¶ms->ap[i]); |
| if (retval) |
| goto fail; |
| |
| i++; |
| } |
| if (i < params->num_ap) { |
| hdd_warn("Number of AP %u less than expected %u", |
| i, params->num_ap); |
| params->num_ap = i; |
| } |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = params->request_id; |
| spin_unlock(&context->context_lock); |
| |
| status = sme_set_significant_change(hdd_ctx->mac_handle, params); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_set_significant_change failed(err=%d)", status); |
| retval = qdf_status_to_os_return(status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hdd_err("sme_set_significant_change timed out"); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&context->context_lock); |
| if (context->request_id == params->request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| hdd_exit(); |
| |
| fail: |
| qdf_mem_free(params); |
| return retval; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_set_significant_change(wiphy, wdev, |
| data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_remove_dsrc_channels () - remove dsrc chanels |
| * @hdd_ctx: hdd context |
| * @wiphy: Pointer to wireless phy |
| * @chan_list: channel list |
| * @num_channels: number of channels |
| * |
| * Return: none |
| */ |
| static void hdd_remove_dsrc_channels(struct hdd_context *hdd_ctx, |
| struct wiphy *wiphy, uint32_t *chan_list, |
| uint8_t *num_channels) |
| { |
| uint8_t num_chan_temp = 0; |
| int i; |
| |
| for (i = 0; i < *num_channels; i++) { |
| if (!wlan_reg_is_dsrc_chan(hdd_ctx->pdev, |
| wlan_reg_freq_to_chan( |
| hdd_ctx->pdev, |
| chan_list[i]))) { |
| chan_list[num_chan_temp] = chan_list[i]; |
| num_chan_temp++; |
| } |
| } |
| *num_channels = num_chan_temp; |
| } |
| |
| /** |
| * hdd_remove_passive_channels () - remove passive channels |
| * @wiphy: Pointer to wireless phy |
| * @chan_list: channel list |
| * @num_channels: number of channels |
| * |
| * Return: none |
| */ |
| static void hdd_remove_passive_channels(struct wiphy *wiphy, |
| uint32_t *chan_list, |
| uint8_t *num_channels) |
| { |
| uint8_t num_chan_temp = 0; |
| int i, j, k; |
| |
| for (i = 0; i < *num_channels; i++) |
| for (j = 0; j < HDD_NUM_NL80211_BANDS; j++) { |
| if (wiphy->bands[j] == NULL) |
| continue; |
| for (k = 0; k < wiphy->bands[j]->n_channels; k++) { |
| if ((chan_list[i] == |
| wiphy->bands[j]->channels[k].center_freq) |
| && (!(wiphy->bands[j]->channels[k].flags & |
| IEEE80211_CHAN_PASSIVE_SCAN)) |
| ) { |
| chan_list[num_chan_temp] = chan_list[i]; |
| num_chan_temp++; |
| } |
| } |
| } |
| |
| *num_channels = num_chan_temp; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_get_valid_channels () - get valid channels |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy, |
| struct wireless_dev |
| *wdev, const void *data, |
| int data_len) |
| { |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| uint32_t chan_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| uint8_t num_channels = 0, i, buf[256] = {0}; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + |
| 1]; |
| uint32_t requestId, maxChannels; |
| tWifiBand wifiBand; |
| QDF_STATUS status; |
| struct sk_buff *reply_skb; |
| int ret, len = 0; |
| |
| /* ENTER_DEV() intentionally not used in a frequently invoked API */ |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| if (wlan_cfg80211_nla_parse(tb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| return -EINVAL; |
| } |
| requestId = |
| nla_get_u32(tb |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| /* Parse and fetch wifi band */ |
| if (!tb |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]) { |
| hdd_err("attr wifi band failed"); |
| return -EINVAL; |
| } |
| wifiBand = |
| nla_get_u32(tb |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]); |
| |
| if (!tb |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]) { |
| hdd_err("attr max channels failed"); |
| return -EINVAL; |
| } |
| maxChannels = |
| nla_get_u32(tb |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]); |
| |
| if (maxChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN) { |
| hdd_err("Max channels %d exceeded Valid channel list len %d", |
| maxChannels, WNI_CFG_VALID_CHANNEL_LIST_LEN); |
| return -EINVAL; |
| } |
| |
| hdd_debug("Req Id: %u Wifi band: %d Max channels: %d", requestId, |
| wifiBand, maxChannels); |
| status = sme_get_valid_channels_by_band(hdd_ctx->mac_handle, |
| wifiBand, chan_list, |
| &num_channels); |
| if (QDF_STATUS_SUCCESS != status) { |
| hdd_err("sme_get_valid_channels_by_band failed (err=%d)", |
| status); |
| return -EINVAL; |
| } |
| |
| num_channels = QDF_MIN(num_channels, maxChannels); |
| |
| hdd_remove_dsrc_channels(hdd_ctx, wiphy, chan_list, &num_channels); |
| if ((QDF_SAP_MODE == adapter->device_mode) || |
| !strncmp(hdd_get_fwpath(), "ap", 2)) |
| hdd_remove_passive_channels(wiphy, chan_list, |
| &num_channels); |
| |
| hdd_debug("Number of channels: %d", num_channels); |
| for (i = 0; i < num_channels; i++) |
| len += scnprintf(buf + len, sizeof(buf) - len, |
| "%u ", chan_list[i]); |
| |
| hdd_debug("Channels: %s", buf); |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + |
| sizeof(u32) * |
| num_channels + |
| NLMSG_HDRLEN); |
| |
| if (reply_skb) { |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_CHANNELS, |
| num_channels) || |
| nla_put(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CHANNELS, |
| sizeof(u32) * num_channels, chan_list)) { |
| hdd_err("nla put fail"); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| ret = cfg80211_vendor_cmd_reply(reply_skb); |
| return ret; |
| } |
| |
| hdd_err("valid channels: buffer alloc fail"); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_valid_channels() - get ext scan valid channels |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_get_valid_channels(wiphy, wdev, data, |
| data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_extscan_update_dwell_time_limits() - update dwell times |
| * @req_msg: Pointer to request message |
| * @bkt_idx: Index of current bucket being processed |
| * @active_min: minimum active dwell time |
| * @active_max: maximum active dwell time |
| * @passive_min: minimum passive dwell time |
| * @passive_max: maximum passive dwell time |
| * |
| * Return: none |
| */ |
| static void hdd_extscan_update_dwell_time_limits( |
| tpSirWifiScanCmdReqParams req_msg, uint32_t bkt_idx, |
| uint32_t active_min, uint32_t active_max, |
| uint32_t passive_min, uint32_t passive_max) |
| { |
| /* update per-bucket dwell times */ |
| if (req_msg->buckets[bkt_idx].min_dwell_time_active > |
| active_min) { |
| req_msg->buckets[bkt_idx].min_dwell_time_active = |
| active_min; |
| } |
| if (req_msg->buckets[bkt_idx].max_dwell_time_active < |
| active_max) { |
| req_msg->buckets[bkt_idx].max_dwell_time_active = |
| active_max; |
| } |
| if (req_msg->buckets[bkt_idx].min_dwell_time_passive > |
| passive_min) { |
| req_msg->buckets[bkt_idx].min_dwell_time_passive = |
| passive_min; |
| } |
| if (req_msg->buckets[bkt_idx].max_dwell_time_passive < |
| passive_max) { |
| req_msg->buckets[bkt_idx].max_dwell_time_passive = |
| passive_max; |
| } |
| /* update dwell-time across all buckets */ |
| if (req_msg->min_dwell_time_active > |
| req_msg->buckets[bkt_idx].min_dwell_time_active) { |
| req_msg->min_dwell_time_active = |
| req_msg->buckets[bkt_idx].min_dwell_time_active; |
| } |
| if (req_msg->max_dwell_time_active < |
| req_msg->buckets[bkt_idx].max_dwell_time_active) { |
| req_msg->max_dwell_time_active = |
| req_msg->buckets[bkt_idx].max_dwell_time_active; |
| } |
| if (req_msg->min_dwell_time_passive > |
| req_msg->buckets[bkt_idx].min_dwell_time_passive) { |
| req_msg->min_dwell_time_passive = |
| req_msg->buckets[bkt_idx].min_dwell_time_passive; |
| } |
| if (req_msg->max_dwell_time_passive > |
| req_msg->buckets[bkt_idx].max_dwell_time_passive) { |
| req_msg->max_dwell_time_passive = |
| req_msg->buckets[bkt_idx].max_dwell_time_passive; |
| } |
| } |
| |
| /** |
| * hdd_extscan_channel_max_reached() - channel max reached |
| * @req: extscan request structure |
| * @total_channels: total number of channels |
| * |
| * Return: true if total channels reached max, false otherwise |
| */ |
| static bool hdd_extscan_channel_max_reached(tSirWifiScanCmdReqParams *req, |
| uint8_t total_channels) |
| { |
| if (total_channels == WLAN_EXTSCAN_MAX_CHANNELS) { |
| hdd_warn( |
| "max #of channels %d reached, take only first %d bucket(s)", |
| total_channels, req->numBuckets); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * hdd_extscan_start_fill_bucket_channel_spec() - fill bucket channel spec |
| * @hdd_ctx: HDD global context |
| * @req_msg: Pointer to request structure |
| * @tb: pointer to NL attributes |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int hdd_extscan_start_fill_bucket_channel_spec( |
| struct hdd_context *hdd_ctx, |
| tpSirWifiScanCmdReqParams req_msg, |
| struct nlattr **tb) |
| { |
| mac_handle_t mac_handle; |
| struct nlattr *bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *buckets; |
| struct nlattr *channels; |
| int rem1, rem2; |
| QDF_STATUS status; |
| uint8_t bkt_index, j, num_channels, total_channels = 0; |
| uint32_t expected_buckets; |
| uint32_t chan_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| |
| uint32_t min_dwell_time_active_bucket = |
| hdd_ctx->config->extscan_active_max_chn_time; |
| uint32_t max_dwell_time_active_bucket = |
| hdd_ctx->config->extscan_active_max_chn_time; |
| uint32_t min_dwell_time_passive_bucket = |
| hdd_ctx->config->extscan_passive_max_chn_time; |
| uint32_t max_dwell_time_passive_bucket = |
| hdd_ctx->config->extscan_passive_max_chn_time; |
| |
| req_msg->min_dwell_time_active = |
| req_msg->max_dwell_time_active = |
| hdd_ctx->config->extscan_active_max_chn_time; |
| |
| req_msg->min_dwell_time_passive = |
| req_msg->max_dwell_time_passive = |
| hdd_ctx->config->extscan_passive_max_chn_time; |
| |
| expected_buckets = req_msg->numBuckets; |
| req_msg->numBuckets = 0; |
| bkt_index = 0; |
| |
| mac_handle = hdd_ctx->mac_handle; |
| nla_for_each_nested(buckets, |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC], rem1) { |
| |
| if (bkt_index >= expected_buckets) { |
| hdd_warn("ignoring excess buckets"); |
| break; |
| } |
| |
| if (wlan_cfg80211_nla_parse(bucket, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| nla_data(buckets), nla_len(buckets), |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch bucket spec */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]) { |
| hdd_err("attr bucket index failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].bucket = nla_get_u8( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]); |
| |
| /* Parse and fetch wifi band */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]) { |
| hdd_err("attr wifi band failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].band = nla_get_u8( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]); |
| |
| /* Parse and fetch period */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]) { |
| hdd_err("attr period failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].period = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]); |
| |
| /* Parse and fetch report events */ |
| if (!bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]) { |
| hdd_err("attr report events failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].reportEvents = nla_get_u8( |
| bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]); |
| |
| /* Parse and fetch max period */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]) { |
| hdd_err("attr max period failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].max_period = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]); |
| |
| /* Parse and fetch base */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE]) { |
| hdd_err("attr base failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].exponent = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE]); |
| |
| /* Parse and fetch step count */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]) { |
| hdd_err("attr step count failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].step_count = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]); |
| hdd_debug("Bucket spec Index: %d Wifi band: %d period: %d report events: %d max period: %u base: %u Step count: %u", |
| req_msg->buckets[bkt_index].bucket, |
| req_msg->buckets[bkt_index].band, |
| req_msg->buckets[bkt_index].period, |
| req_msg->buckets[bkt_index].reportEvents, |
| req_msg->buckets[bkt_index].max_period, |
| req_msg->buckets[bkt_index].exponent, |
| req_msg->buckets[bkt_index].step_count); |
| |
| /* start with known good values for bucket dwell times */ |
| req_msg->buckets[bkt_index].min_dwell_time_active = |
| req_msg->buckets[bkt_index].max_dwell_time_active = |
| hdd_ctx->config->extscan_active_max_chn_time; |
| |
| req_msg->buckets[bkt_index].min_dwell_time_passive = |
| req_msg->buckets[bkt_index].max_dwell_time_passive = |
| hdd_ctx->config->extscan_passive_max_chn_time; |
| |
| /* Framework shall pass the channel list if the input WiFi band |
| * is WIFI_BAND_UNSPECIFIED. |
| * If the input WiFi band is specified (any value other than |
| * WIFI_BAND_UNSPECIFIED) then driver populates the channel list |
| */ |
| if (req_msg->buckets[bkt_index].band != WIFI_BAND_UNSPECIFIED) { |
| if (hdd_extscan_channel_max_reached(req_msg, |
| total_channels)) |
| return 0; |
| |
| num_channels = 0; |
| hdd_debug("WiFi band is specified, driver to fill channel list"); |
| status = sme_get_valid_channels_by_band(mac_handle, |
| req_msg->buckets[bkt_index].band, |
| chan_list, &num_channels); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_GetValidChannelsByBand failed (err=%d)", |
| status); |
| return -EINVAL; |
| } |
| hdd_debug("before trimming, num_channels: %d", |
| num_channels); |
| |
| req_msg->buckets[bkt_index].numChannels = |
| QDF_MIN(num_channels, |
| (WLAN_EXTSCAN_MAX_CHANNELS - |
| total_channels)); |
| hdd_debug("Adj Num channels/bucket: %d total_channels: %d", |
| req_msg->buckets[bkt_index].numChannels, |
| total_channels); |
| total_channels += |
| req_msg->buckets[bkt_index].numChannels; |
| |
| for (j = 0; j < req_msg->buckets[bkt_index].numChannels; |
| j++) { |
| req_msg->buckets[bkt_index].channels[j].channel = |
| chan_list[j]; |
| req_msg->buckets[bkt_index].channels[j]. |
| chnlClass = 0; |
| if ((wlan_reg_get_channel_state( |
| hdd_ctx->pdev, |
| cds_freq_to_chan(chan_list[j]))) != |
| CHANNEL_STATE_ENABLE) { |
| req_msg->buckets[bkt_index].channels[j]. |
| passive = 1; |
| req_msg->buckets[bkt_index].channels[j]. |
| dwellTimeMs = |
| hdd_ctx->config-> |
| extscan_passive_max_chn_time; |
| /* reconfigure per-bucket dwell time */ |
| if (min_dwell_time_passive_bucket > |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| min_dwell_time_passive_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| if (max_dwell_time_passive_bucket < |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| max_dwell_time_passive_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| |
| } else { |
| req_msg->buckets[bkt_index].channels[j]. |
| passive = 0; |
| req_msg->buckets[bkt_index].channels[j]. |
| dwellTimeMs = |
| hdd_ctx->config->extscan_active_max_chn_time; |
| /* reconfigure per-bucket dwell times */ |
| if (min_dwell_time_active_bucket > |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| min_dwell_time_active_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| if (max_dwell_time_active_bucket < |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| max_dwell_time_active_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| |
| } |
| |
| hdd_debug("Channel: %u Passive: %u Dwell time: %u ms Class: %u", |
| req_msg->buckets[bkt_index].channels[j].channel, |
| req_msg->buckets[bkt_index].channels[j].passive, |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs, |
| req_msg->buckets[bkt_index].channels[j].chnlClass); |
| } |
| |
| hdd_extscan_update_dwell_time_limits( |
| req_msg, bkt_index, |
| min_dwell_time_active_bucket, |
| max_dwell_time_active_bucket, |
| min_dwell_time_passive_bucket, |
| max_dwell_time_passive_bucket); |
| |
| hdd_debug("bkt_index:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d", |
| bkt_index, |
| req_msg->buckets[bkt_index].min_dwell_time_active, |
| req_msg->buckets[bkt_index].max_dwell_time_active, |
| req_msg->buckets[bkt_index].min_dwell_time_passive, |
| req_msg->buckets[bkt_index].max_dwell_time_passive); |
| |
| bkt_index++; |
| req_msg->numBuckets++; |
| continue; |
| } |
| |
| /* Parse and fetch number of channels */ |
| if (!bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]) { |
| hdd_err("attr num channels failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].numChannels = |
| nla_get_u32(bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]); |
| hdd_debug("before trimming: num channels %d", |
| req_msg->buckets[bkt_index].numChannels); |
| |
| req_msg->buckets[bkt_index].numChannels = |
| QDF_MIN(req_msg->buckets[bkt_index].numChannels, |
| (WLAN_EXTSCAN_MAX_CHANNELS - total_channels)); |
| hdd_debug("Num channels/bucket: %d total_channels: %d", |
| req_msg->buckets[bkt_index].numChannels, |
| total_channels); |
| if (hdd_extscan_channel_max_reached(req_msg, total_channels)) |
| return 0; |
| |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC]) { |
| hdd_err("attr channel spec failed"); |
| return -EINVAL; |
| } |
| |
| j = 0; |
| nla_for_each_nested(channels, |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC], rem2) { |
| if ((j >= req_msg->buckets[bkt_index].numChannels) || |
| hdd_extscan_channel_max_reached(req_msg, |
| total_channels)) |
| break; |
| |
| if (wlan_cfg80211_nla_parse(channel, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| nla_data(channels), nla_len(channels), |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch channel */ |
| if (!channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]) { |
| hdd_err("attr channel failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].channels[j].channel = |
| nla_get_u32(channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]); |
| hdd_debug("channel %u", |
| req_msg->buckets[bkt_index].channels[j].channel); |
| |
| /* Parse and fetch dwell time */ |
| if (!channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]) { |
| hdd_err("attr dwelltime failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs = |
| nla_get_u32(channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]); |
| |
| /* Override dwell time if required */ |
| if (req_msg->buckets[bkt_index].channels[j].dwellTimeMs < |
| hdd_ctx->config->extscan_active_min_chn_time || |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs > |
| hdd_ctx->config->extscan_active_max_chn_time) { |
| hdd_debug("WiFi band is unspecified, dwellTime:%d", |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs); |
| |
| if ((wlan_reg_get_channel_state( |
| hdd_ctx->pdev, |
| cds_freq_to_chan( |
| req_msg->buckets[bkt_index]. |
| channels[j].channel))) |
| != CHANNEL_STATE_ENABLE) { |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs = |
| hdd_ctx->config->extscan_passive_max_chn_time; |
| } else { |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs = |
| hdd_ctx->config->extscan_active_max_chn_time; |
| } |
| } |
| |
| hdd_debug("New Dwell time %u ms", |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs); |
| |
| if ((wlan_reg_get_channel_state(hdd_ctx->pdev, |
| cds_freq_to_chan( |
| req_msg->buckets[bkt_index]. |
| channels[j].channel))) |
| != CHANNEL_STATE_ENABLE) { |
| if (min_dwell_time_passive_bucket > |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| min_dwell_time_passive_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| if (max_dwell_time_passive_bucket < |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| max_dwell_time_passive_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| } else { |
| if (min_dwell_time_active_bucket > |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| min_dwell_time_active_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| if (max_dwell_time_active_bucket < |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs) { |
| max_dwell_time_active_bucket = |
| req_msg->buckets[bkt_index].channels[j].dwellTimeMs; |
| } |
| } |
| |
| /* Parse and fetch channel spec passive */ |
| if (!channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]) { |
| hdd_err("attr channel spec passive failed"); |
| return -EINVAL; |
| } |
| req_msg->buckets[bkt_index].channels[j].passive = |
| nla_get_u8(channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]); |
| hdd_debug("Chnl spec passive %u", |
| req_msg->buckets[bkt_index].channels[j].passive); |
| /* Override scan type if required */ |
| if ((wlan_reg_get_channel_state(hdd_ctx->pdev, |
| cds_freq_to_chan( |
| req_msg->buckets[bkt_index]. |
| channels[j].channel))) |
| != CHANNEL_STATE_ENABLE) { |
| req_msg->buckets[bkt_index].channels[j].passive = true; |
| } else { |
| req_msg->buckets[bkt_index].channels[j].passive = false; |
| } |
| j++; |
| total_channels++; |
| } |
| |
| if (j != req_msg->buckets[bkt_index].numChannels) { |
| hdd_err("Input parameters didn't match"); |
| return -EINVAL; |
| } |
| |
| hdd_extscan_update_dwell_time_limits( |
| req_msg, bkt_index, |
| min_dwell_time_active_bucket, |
| max_dwell_time_active_bucket, |
| min_dwell_time_passive_bucket, |
| max_dwell_time_passive_bucket); |
| |
| hdd_debug("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d", |
| bkt_index, |
| req_msg->buckets[bkt_index].min_dwell_time_active, |
| req_msg->buckets[bkt_index].max_dwell_time_active, |
| req_msg->buckets[bkt_index].min_dwell_time_passive, |
| req_msg->buckets[bkt_index].max_dwell_time_passive); |
| |
| bkt_index++; |
| req_msg->numBuckets++; |
| } |
| |
| hdd_debug("Global: actv_min:%d actv_max:%d pass_min:%d pass_max:%d", |
| req_msg->min_dwell_time_active, |
| req_msg->max_dwell_time_active, |
| req_msg->min_dwell_time_passive, |
| req_msg->max_dwell_time_passive); |
| return 0; |
| } |
| |
| /* |
| * hdd_extscan_map_usr_drv_config_flags() - map userspace to driver config flags |
| * @config_flags - [input] configuration flags. |
| * |
| * This function maps user space received configuration flags to |
| * driver representation. |
| * |
| * Return: configuration flags |
| */ |
| static uint32_t hdd_extscan_map_usr_drv_config_flags(uint32_t config_flags) |
| { |
| uint32_t configuration_flags = 0; |
| |
| if (config_flags & EXTSCAN_LP_EXTENDED_BATCHING) |
| configuration_flags |= EXTSCAN_LP_EXTENDED_BATCHING; |
| |
| return configuration_flags; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_extscan_start() |
| */ |
| #define PARAM_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define PARAM_BASE_PERIOD \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD |
| #define PARAM_MAX_AP_PER_SCAN \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN |
| #define PARAM_RPT_THRHLD_PERCENT \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT |
| #define PARAM_RPT_THRHLD_NUM_SCANS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS |
| #define PARAM_NUM_BUCKETS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS |
| #define PARAM_CONFIG_FLAGS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_start() - ext scan start |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * Return: 0 on success; error number otherwise |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirWifiScanCmdReqParams pReqMsg; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| uint32_t request_id, num_buckets; |
| QDF_STATUS status; |
| int retval; |
| unsigned long rc; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (QDF_NDI_MODE == adapter->device_mode) { |
| hdd_err("Command not allowed for NDI interface"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| pReqMsg = qdf_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hdd_err("memory allocation failed"); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = adapter->session_id; |
| |
| /* Parse and fetch base period */ |
| if (!tb[PARAM_BASE_PERIOD]) { |
| hdd_err("attr base period failed"); |
| goto fail; |
| } |
| pReqMsg->basePeriod = nla_get_u32(tb[PARAM_BASE_PERIOD]); |
| |
| /* Parse and fetch max AP per scan */ |
| if (!tb[PARAM_MAX_AP_PER_SCAN]) { |
| hdd_err("attr max_ap_per_scan failed"); |
| goto fail; |
| } |
| pReqMsg->maxAPperScan = nla_get_u32(tb[PARAM_MAX_AP_PER_SCAN]); |
| |
| /* Parse and fetch report threshold percent */ |
| if (!tb[PARAM_RPT_THRHLD_PERCENT]) { |
| hdd_err("attr report_threshold percent failed"); |
| goto fail; |
| } |
| pReqMsg->report_threshold_percent = nla_get_u8(tb[PARAM_RPT_THRHLD_PERCENT]); |
| |
| /* Parse and fetch report threshold num scans */ |
| if (!tb[PARAM_RPT_THRHLD_NUM_SCANS]) { |
| hdd_err("attr report_threshold num scans failed"); |
| goto fail; |
| } |
| pReqMsg->report_threshold_num_scans = nla_get_u8(tb[PARAM_RPT_THRHLD_NUM_SCANS]); |
| hdd_debug("Req Id: %d Session Id: %d Base Period: %d Max AP per Scan: %d Report Threshold percent: %d Report Threshold num scans: %d", |
| pReqMsg->requestId, pReqMsg->sessionId, |
| pReqMsg->basePeriod, pReqMsg->maxAPperScan, |
| pReqMsg->report_threshold_percent, |
| pReqMsg->report_threshold_num_scans); |
| |
| /* Parse and fetch number of buckets */ |
| if (!tb[PARAM_NUM_BUCKETS]) { |
| hdd_err("attr number of buckets failed"); |
| goto fail; |
| } |
| num_buckets = nla_get_u8(tb[PARAM_NUM_BUCKETS]); |
| if (num_buckets > WLAN_EXTSCAN_MAX_BUCKETS) { |
| hdd_warn("Exceeded MAX number of buckets: %d", |
| WLAN_EXTSCAN_MAX_BUCKETS); |
| num_buckets = WLAN_EXTSCAN_MAX_BUCKETS; |
| } |
| hdd_debug("Input: Number of Buckets %d", num_buckets); |
| pReqMsg->numBuckets = num_buckets; |
| |
| /* This is optional attribute, if not present set it to 0 */ |
| if (!tb[PARAM_CONFIG_FLAGS]) |
| pReqMsg->configuration_flags = 0; |
| else |
| pReqMsg->configuration_flags = |
| hdd_extscan_map_usr_drv_config_flags( |
| nla_get_u32(tb[PARAM_CONFIG_FLAGS])); |
| |
| pReqMsg->extscan_adaptive_dwell_mode = |
| hdd_ctx->config->extscan_adaptive_dwell_mode; |
| |
| hdd_debug("Configuration flags: %u", |
| pReqMsg->configuration_flags); |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC]) { |
| hdd_err("attr bucket spec failed"); |
| goto fail; |
| } |
| |
| if (hdd_extscan_start_fill_bucket_channel_spec(hdd_ctx, pReqMsg, tb)) |
| goto fail; |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| context->buckets_scanned = 0; |
| spin_unlock(&context->context_lock); |
| |
| status = sme_ext_scan_start(hdd_ctx->mac_handle, pReqMsg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_ext_scan_start failed(err=%d)", status); |
| goto fail; |
| } |
| |
| hdd_ctx->ext_scan_start_since_boot = qdf_get_monotonic_boottime(); |
| hdd_debug("Timestamp since boot: %llu", |
| hdd_ctx->ext_scan_start_since_boot); |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hdd_err("sme_ext_scan_start timed out"); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&context->context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| hdd_exit(); |
| return retval; |
| |
| fail: |
| qdf_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_extscan_start() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_BASE_PERIOD |
| #undef PARAMS_MAX_AP_PER_SCAN |
| #undef PARAMS_RPT_THRHLD_PERCENT |
| #undef PARAMS_RPT_THRHLD_NUM_SCANS |
| #undef PARAMS_NUM_BUCKETS |
| #undef PARAM_CONFIG_FLAGS |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_start() - start extscan |
| * @wiphy: Pointer to wireless phy. |
| * @wdev: Pointer to wireless device. |
| * @data: Pointer to input data. |
| * @data_len: Length of @data. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_start(wiphy, wdev, data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_extscan_stop() |
| */ |
| #define PARAM_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_stop() - ext scan stop |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| tpSirExtScanStopReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| QDF_STATUS status; |
| uint32_t request_id; |
| int retval; |
| unsigned long rc; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| pReqMsg = qdf_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = adapter->session_id; |
| hdd_debug("Req Id %d Session Id %d", |
| pReqMsg->requestId, pReqMsg->sessionId); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| spin_unlock(&context->context_lock); |
| |
| status = sme_ext_scan_stop(hdd_ctx->mac_handle, pReqMsg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_ext_scan_stop failed(err=%d)", status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hdd_err("sme_ext_scan_stop timed out"); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&context->context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| hdd_exit(); |
| return retval; |
| |
| fail: |
| qdf_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_stop() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_stop() - stop extscan |
| * @wiphy: Pointer to wireless phy. |
| * @wdev: Pointer to wireless device. |
| * @data: Pointer to input data. |
| * @data_len: Length of @data. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_stop(wiphy, wdev, data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hotlist |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct extscan_bssid_hotlist_reset_params params; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| QDF_STATUS status; |
| int id, retval; |
| unsigned long rc; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch request Id */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; |
| if (!tb[id]) { |
| hdd_err("attr request id failed"); |
| return -EINVAL; |
| } |
| |
| params.request_id = nla_get_u32(tb[id]); |
| params.vdev_id = adapter->session_id; |
| hdd_debug("Req Id %d vdev Id %d", params.request_id, params.vdev_id); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = params.request_id; |
| spin_unlock(&context->context_lock); |
| |
| status = sme_reset_bss_hotlist(hdd_ctx->mac_handle, ¶ms); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_reset_bss_hotlist failed(err=%d)", status); |
| return qdf_status_to_os_return(status); |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout |
| (&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| if (!rc) { |
| hdd_err("sme_reset_bss_hotlist timed out"); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&context->context_lock); |
| if (context->request_id == params.request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| hdd_exit(); |
| return retval; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hot list |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(wiphy, wdev, |
| data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_reset_significant_change() - |
| * reset significant change |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: none |
| */ |
| static int |
| __wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct extscan_capabilities_reset_params params; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| QDF_STATUS status; |
| int id, retval; |
| unsigned long rc; |
| |
| hdd_enter_dev(dev); |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch request Id */ |
| id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; |
| if (!tb[id]) { |
| hdd_err("attr request id failed"); |
| return -EINVAL; |
| } |
| |
| params.request_id = nla_get_u32(tb[id]); |
| params.vdev_id = adapter->session_id; |
| hdd_debug("Req Id %d Vdev Id %d", params.request_id, params.vdev_id); |
| |
| context = &ext_scan_context; |
| spin_lock(&context->context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = params.request_id; |
| spin_unlock(&context->context_lock); |
| |
| status = sme_reset_significant_change(hdd_ctx->mac_handle, ¶ms); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_reset_significant_change failed(err=%d)", |
| status); |
| return -EINVAL; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hdd_err("sme_ResetSignificantChange timed out"); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&context->context_lock); |
| if (context->request_id == params.request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&context->context_lock); |
| } |
| hdd_exit(); |
| return retval; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_reset_significant_change() - reset significant |
| * change |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| int wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_reset_significant_change(wiphy, wdev, |
| data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * hdd_extscan_epno_fill_network_list() - epno fill network list |
| * @hddctx: HDD context |
| * @req_msg: request message |
| * @tb: vendor attribute table |
| * |
| * This function reads the network block NL vendor attributes from %tb and |
| * fill in the epno request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_extscan_epno_fill_network_list( |
| struct hdd_context *hddctx, |
| struct wifi_epno_params *req_msg, |
| struct nlattr **tb) |
| { |
| struct nlattr *network[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| struct nlattr *networks; |
| int rem1, ssid_len; |
| uint8_t index, *ssid; |
| uint32_t expected_networks; |
| |
| expected_networks = req_msg->num_networks; |
| index = 0; |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST]) { |
| hdd_err("attr networks list failed"); |
| return -EINVAL; |
| } |
| nla_for_each_nested(networks, |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST], |
| rem1) { |
| |
| if (index == expected_networks) { |
| hdd_warn("ignoring excess networks"); |
| break; |
| } |
| |
| if (wlan_cfg80211_nla_parse(network, |
| QCA_WLAN_VENDOR_ATTR_PNO_MAX, |
| nla_data(networks), |
| nla_len(networks), |
| wlan_hdd_pno_config_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch ssid */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]) { |
| hdd_err("attr network ssid failed"); |
| return -EINVAL; |
| } |
| ssid_len = nla_len( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]); |
| |
| /* nla_parse will detect overflow but not underflow */ |
| if (0 == ssid_len) { |
| hdd_err("zero ssid length"); |
| return -EINVAL; |
| } |
| |
| /* Decrement by 1, don't count null character */ |
| ssid_len--; |
| |
| req_msg->networks[index].ssid.length = ssid_len; |
| hdd_debug("network ssid length %d", ssid_len); |
| ssid = nla_data(network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]); |
| qdf_mem_copy(req_msg->networks[index].ssid.ssId, |
| ssid, ssid_len); |
| hdd_debug("Ssid (%.*s)", |
| req_msg->networks[index].ssid.length, |
| req_msg->networks[index].ssid.ssId); |
| |
| /* Parse and fetch epno flags */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS]) { |
| hdd_err("attr epno flags failed"); |
| return -EINVAL; |
| } |
| req_msg->networks[index].flags = nla_get_u8( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS]); |
| hdd_debug("flags %u", req_msg->networks[index].flags); |
| |
| /* Parse and fetch auth bit */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT]) { |
| hdd_err("attr auth bit failed"); |
| return -EINVAL; |
| } |
| req_msg->networks[index].auth_bit_field = nla_get_u8( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT]); |
| hdd_debug("auth bit %u", |
| req_msg->networks[index].auth_bit_field); |
| |
| index++; |
| } |
| req_msg->num_networks = index; |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_epno_list() - epno set network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the epno request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct wifi_epno_params *req_msg = NULL; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[ |
| QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| QDF_STATUS status; |
| uint32_t num_networks, len; |
| int ret_val; |
| |
| hdd_enter_dev(dev); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (!hdd_ctx->config->extscan_enabled) { |
| hdd_err("extscan not supported"); |
| return -ENOTSUPP; |
| } |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, |
| data_len, wlan_hdd_pno_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch number of networks */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS]) { |
| hdd_err("attr num networks failed"); |
| return -EINVAL; |
| } |
| |
| /* |
| * num_networks is also used as EPNO SET/RESET request. |
| * if num_networks is zero then it is treated as RESET. |
| */ |
| num_networks = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS]); |
| |
| if (num_networks > MAX_EPNO_NETWORKS) { |
| hdd_debug("num of nw: %d exceeded max: %d, resetting to: %d", |
| num_networks, MAX_EPNO_NETWORKS, MAX_EPNO_NETWORKS); |
| num_networks = MAX_EPNO_NETWORKS; |
| } |
| |
| hdd_debug("num networks %u", num_networks); |
| len = sizeof(*req_msg) + |
| (num_networks * sizeof(struct wifi_epno_network)); |
| |
| req_msg = qdf_mem_malloc(len); |
| if (!req_msg) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| req_msg->num_networks = num_networks; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| req_msg->request_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID]); |
| hdd_debug("Req Id %u", req_msg->request_id); |
| |
| req_msg->session_id = adapter->session_id; |
| hdd_debug("Session Id %d", req_msg->session_id); |
| |
| if (num_networks) { |
| |
| /* Parse and fetch min_5ghz_rssi */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI]) { |
| hdd_err("min_5ghz_rssi id failed"); |
| goto fail; |
| } |
| req_msg->min_5ghz_rssi = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI]); |
| |
| /* Parse and fetch min_24ghz_rssi */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI]) { |
| hdd_err("min_24ghz_rssi id failed"); |
| goto fail; |
| } |
| req_msg->min_24ghz_rssi = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI]); |
| |
| /* Parse and fetch initial_score_max */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX]) { |
| hdd_err("initial_score_max id failed"); |
| goto fail; |
| } |
| req_msg->initial_score_max = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX]); |
| |
| /* Parse and fetch current_connection_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS]) { |
| hdd_err("current_connection_bonus id failed"); |
| goto fail; |
| } |
| req_msg->current_connection_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS] |
| ); |
| |
| /* Parse and fetch same_network_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS]) { |
| hdd_err("same_network_bonus id failed"); |
| goto fail; |
| } |
| req_msg->same_network_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS]); |
| |
| /* Parse and fetch secure_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS]) { |
| hdd_err("secure_bonus id failed"); |
| goto fail; |
| } |
| req_msg->secure_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS]); |
| |
| /* Parse and fetch band_5ghz_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS]) { |
| hdd_err("band_5ghz_bonus id failed"); |
| goto fail; |
| } |
| req_msg->band_5ghz_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS]); |
| |
| hdd_debug("min_5ghz_rssi: %d min_24ghz_rssi: %d", |
| req_msg->min_5ghz_rssi, |
| req_msg->min_24ghz_rssi); |
| hdd_debug("initial_score_max: %d current_connection_bonus:%d", |
| req_msg->initial_score_max, |
| req_msg->current_connection_bonus); |
| hdd_debug("Bonuses same_network: %d secure: %d band_5ghz: %d", |
| req_msg->same_network_bonus, |
| req_msg->secure_bonus, |
| req_msg->band_5ghz_bonus); |
| |
| if (hdd_extscan_epno_fill_network_list(hdd_ctx, req_msg, tb)) |
| goto fail; |
| |
| } |
| |
| status = sme_set_epno_list(hdd_ctx->mac_handle, req_msg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_set_epno_list failed(err=%d)", status); |
| goto fail; |
| } |
| |
| hdd_exit(); |
| qdf_mem_free(req_msg); |
| return 0; |
| |
| fail: |
| qdf_mem_free(req_msg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_epno_list() - epno set network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the epno request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| int wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_epno_list(wiphy, wdev, |
| data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #define PARAM_ID QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID |
| #define PARAM_REALM QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM |
| #define PARAM_ROAM_ID \ |
| QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID |
| #define PARAM_ROAM_PLMN \ |
| QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN |
| |
| /** |
| * hdd_extscan_passpoint_fill_network_list() - passpoint fill network list |
| * @hddctx: HDD context |
| * @req_msg: request message |
| * @tb: vendor attribute table |
| * |
| * This function reads the network block NL vendor attributes from %tb and |
| * fill in the passpoint request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_extscan_passpoint_fill_network_list( |
| struct hdd_context *hddctx, |
| struct wifi_passpoint_req *req_msg, |
| struct nlattr **tb) |
| { |
| struct nlattr *network[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| struct nlattr *networks; |
| int rem1; |
| size_t len; |
| uint8_t index; |
| uint32_t expected_networks; |
| |
| expected_networks = req_msg->num_networks; |
| index = 0; |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY]) { |
| hdd_err("attr network array failed"); |
| return -EINVAL; |
| } |
| nla_for_each_nested(networks, |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY], |
| rem1) { |
| |
| if (index == expected_networks) { |
| hdd_warn("ignoring excess networks"); |
| break; |
| } |
| |
| if (wlan_cfg80211_nla_parse(network, |
| QCA_WLAN_VENDOR_ATTR_PNO_MAX, |
| nla_data(networks), |
| nla_len(networks), |
| wlan_hdd_pno_config_policy)) { |
| hdd_err("nla_parse failed"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch identifier */ |
| if (!network[PARAM_ID]) { |
| hdd_err("attr passpoint id failed"); |
| return -EINVAL; |
| } |
| req_msg->networks[index].id = nla_get_u32(network[PARAM_ID]); |
| hdd_debug("Id %u", req_msg->networks[index].id); |
| |
| /* Parse and fetch realm */ |
| if (!network[PARAM_REALM]) { |
| hdd_err("attr realm failed"); |
| return -EINVAL; |
| } |
| len = nla_strlcpy(req_msg->networks[index].realm, |
| network[PARAM_REALM], |
| SIR_PASSPOINT_REALM_LEN); |
| /* Don't send partial realm to firmware */ |
| if (len >= SIR_PASSPOINT_REALM_LEN) { |
| hdd_err("user passed invalid realm, len:%zu", len); |
| return -EINVAL; |
| } |
| |
| hdd_debug("realm: %s", req_msg->networks[index].realm); |
| |
| /* Parse and fetch roaming consortium ids */ |
| if (!network[PARAM_ROAM_ID]) { |
| hdd_err("attr roaming consortium ids failed"); |
| return -EINVAL; |
| } |
| nla_memcpy(&req_msg->networks[index].roaming_consortium_ids, |
| network[PARAM_ROAM_ID], |
| sizeof(req_msg->networks[0].roaming_consortium_ids)); |
| hdd_debug("roaming consortium ids"); |
| |
| /* Parse and fetch plmn */ |
| if (!network[PARAM_ROAM_PLMN]) { |
| hdd_err("attr plmn failed"); |
| return -EINVAL; |
| } |
| nla_memcpy(&req_msg->networks[index].plmn, |
| network[PARAM_ROAM_PLMN], |
| SIR_PASSPOINT_PLMN_LEN); |
| hdd_debug("plmn %02x:%02x:%02x)", |
| req_msg->networks[index].plmn[0], |
| req_msg->networks[index].plmn[1], |
| req_msg->networks[index].plmn[2]); |
| |
| index++; |
| } |
| req_msg->num_networks = index; |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the passpoint request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct wifi_passpoint_req *req_msg = NULL; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| QDF_STATUS status; |
| uint32_t num_networks = 0; |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, |
| data_len, wlan_hdd_pno_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch number of networks */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM]) { |
| hdd_err("attr num networks failed"); |
| return -EINVAL; |
| } |
| num_networks = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM]); |
| if (num_networks > SIR_PASSPOINT_LIST_MAX_NETWORKS) { |
| hdd_err("num networks %u exceeds max %u", |
| num_networks, SIR_PASSPOINT_LIST_MAX_NETWORKS); |
| return -EINVAL; |
| } |
| |
| hdd_debug("num networks %u", num_networks); |
| |
| req_msg = qdf_mem_malloc(sizeof(*req_msg) + |
| (num_networks * sizeof(req_msg->networks[0]))); |
| if (!req_msg) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| req_msg->num_networks = num_networks; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| req_msg->request_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| req_msg->session_id = adapter->session_id; |
| hdd_debug("Req Id %u Session Id %d", req_msg->request_id, |
| req_msg->session_id); |
| |
| if (hdd_extscan_passpoint_fill_network_list(hdd_ctx, req_msg, tb)) |
| goto fail; |
| |
| status = sme_set_passpoint_list(hdd_ctx->mac_handle, req_msg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_set_passpoint_list failed(err=%d)", status); |
| goto fail; |
| } |
| |
| hdd_exit(); |
| qdf_mem_free(req_msg); |
| return 0; |
| |
| fail: |
| qdf_mem_free(req_msg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the passpoint request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| int wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_passpoint_list(wiphy, wdev, |
| data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function resets passpoint networks list |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct wifi_passpoint_req *req_msg = NULL; |
| struct net_device *dev = wdev->netdev; |
| struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct hdd_context *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| QDF_STATUS status; |
| int ret; |
| |
| hdd_enter_dev(dev); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { |
| hdd_err("Command not allowed in FTM mode"); |
| return -EPERM; |
| } |
| |
| if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, |
| data_len, wlan_hdd_extscan_config_policy)) { |
| hdd_err("Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| req_msg = qdf_mem_malloc(sizeof(*req_msg)); |
| if (!req_msg) { |
| hdd_err("qdf_mem_malloc failed"); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hdd_err("attr request id failed"); |
| goto fail; |
| } |
| req_msg->request_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| req_msg->session_id = adapter->session_id; |
| hdd_debug("Req Id %u Session Id %d", |
| req_msg->request_id, req_msg->session_id); |
| |
| status = sme_reset_passpoint_list(hdd_ctx->mac_handle, req_msg); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| hdd_err("sme_reset_passpoint_list failed(err=%d)", status); |
| goto fail; |
| } |
| |
| hdd_exit(); |
| qdf_mem_free(req_msg); |
| return 0; |
| |
| fail: |
| qdf_mem_free(req_msg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function resets passpoint networks list |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| int wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| cds_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_reset_passpoint_list(wiphy, wdev, |
| data, data_len); |
| cds_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #undef PARAM_ID |
| #undef PARAM_REALM |
| #undef PARAM_ROAM_ID |
| #undef PARAM_ROAM_PLMN |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_init() - Initialize the ExtScan feature |
| * @hdd_ctx: Global HDD context |
| * |
| * Return: none |
| */ |
| void wlan_hdd_cfg80211_extscan_init(struct hdd_context *hdd_ctx) |
| { |
| init_completion(&ext_scan_context.response_event); |
| spin_lock_init(&ext_scan_context.context_lock); |
| } |
| |
| #endif /* FEATURE_WLAN_EXTSCAN */ |