1 /* 2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /** 20 * DOC: wlan_hdd_ext_scan.c 21 * 22 * WLAN Host Device Driver EXT SCAN feature implementation 23 * 24 */ 25 26 #ifdef FEATURE_WLAN_EXTSCAN 27 28 #include "osif_sync.h" 29 #include "wlan_hdd_ext_scan.h" 30 #include "wlan_hdd_regulatory.h" 31 #include "cds_utils.h" 32 #include "cds_sched.h" 33 #include <qca_vendor.h> 34 #include "wlan_extscan_ucfg_api.h" 35 #include "wlan_hdd_scan.h" 36 37 /* amount of time to wait for a synchronous request/response operation */ 38 #define WLAN_WAIT_TIME_EXTSCAN 1000 39 40 /** 41 * struct hdd_ext_scan_context - hdd ext scan context 42 * @request_id: userspace-assigned ID associated with the request 43 * @response_event: Ext scan wait event 44 * @response_status: Status returned by FW in response to a request 45 * @ignore_cached_results: Flag to ignore cached results or not 46 * @context_lock: Spinlock to serialize all context accesses 47 * @capability_response: Ext scan capability response data from target 48 * @buckets_scanned: bitmask of buckets scanned in extscan cycle 49 */ 50 struct hdd_ext_scan_context { 51 uint32_t request_id; 52 int response_status; 53 bool ignore_cached_results; 54 struct completion response_event; 55 spinlock_t context_lock; 56 struct ext_scan_capabilities_response capability_response; 57 uint32_t buckets_scanned; 58 }; 59 static struct hdd_ext_scan_context ext_scan_context; 60 61 const struct nla_policy 62 wlan_hdd_extscan_config_policy[EXTSCAN_PARAM_MAX + 1] = { 63 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID] = { 64 .type = NLA_U32}, 65 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL] = {.type = NLA_U32}, 66 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME] = { 67 .type = NLA_U32}, 68 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE] = {.type = NLA_U8}, 69 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CLASS] = {.type = NLA_U8}, 70 71 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX] = {.type = NLA_U8}, 72 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND] = {.type = NLA_U8}, 73 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD] = {.type = NLA_U32}, 74 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS] = { 75 .type = NLA_U8}, 76 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS] = { 77 .type = NLA_U32}, 78 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD] = { 79 .type = NLA_U32}, 80 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN] = { 81 .type = NLA_U32}, 82 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT] = { 83 .type = NLA_U8}, 84 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS] = { 85 .type = NLA_U8 }, 86 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS] = { 87 .type = NLA_U8}, 88 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH] = { 89 .type = NLA_U8}, 90 91 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX] = { 92 .type = NLA_U32}, 93 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID] = 94 VENDOR_NLA_POLICY_MAC_ADDR, 95 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW] = { 96 .type = NLA_S32}, 97 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH] = { 98 .type = NLA_S32}, 99 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_CHANNEL] = { 100 .type = NLA_U32}, 101 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP] = { 102 .type = NLA_U32}, 103 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE] = { 104 .type = NLA_U32}, 105 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE] = { 106 .type = NLA_U32}, 107 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING] = { 108 .type = NLA_U32}, 109 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP] = { 110 .type = NLA_U32}, 111 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD] = { 112 .type = NLA_U32}, 113 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE] = { 114 .type = NLA_U32}, 115 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT] = { 116 .type = NLA_U32}, 117 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_SSID] = { 118 .type = NLA_BINARY, 119 .len = IEEE80211_MAX_SSID_LEN + 1 }, 120 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE] = { 121 .type = NLA_U32 }, 122 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_NUM_SSID] = { 123 .type = NLA_U32 }, 124 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_BAND] = { 125 .type = NLA_U8 }, 126 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW] = { 127 .type = NLA_S32 }, 128 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH] = { 129 .type = NLA_S32 }, 130 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS] = { 131 .type = NLA_U32 }, 132 [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE] = { 133 .type = NLA_U32}, 134 }; 135 136 const struct nla_policy 137 wlan_hdd_pno_config_policy[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1] = { 138 [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM] = { 139 .type = NLA_U32 140 }, 141 [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID] = { 142 .type = NLA_U32 143 }, 144 [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS] = { 145 .type = NLA_U32 146 }, 147 [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID] = { 148 .type = NLA_BINARY, 149 .len = IEEE80211_MAX_SSID_LEN + 1 150 }, 151 [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS] = { 152 .type = NLA_U8 153 }, 154 [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT] = { 155 .type = NLA_U8 156 }, 157 [QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI] = { 158 .type = NLA_U32 159 }, 160 [QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI] = { 161 .type = NLA_U32 162 }, 163 [QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX] = { 164 .type = NLA_U32 165 }, 166 [QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS] = { 167 .type = NLA_U32 168 }, 169 [QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS] = { 170 .type = NLA_U32 171 }, 172 [QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS] = { 173 .type = NLA_U32 174 }, 175 [QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS] = { 176 .type = NLA_U32 177 }, 178 [QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID] = { 179 .type = NLA_U32 180 }, 181 }; 182 183 /** 184 * wlan_hdd_cfg80211_extscan_get_capabilities_rsp() - response from target 185 * @hdd_ctx: Pointer to hdd context 186 * @data: Pointer to ext scan capabilities response from fw 187 * 188 * Return: None 189 */ 190 static void 191 wlan_hdd_cfg80211_extscan_get_capabilities_rsp(struct hdd_context *hdd_ctx, 192 struct ext_scan_capabilities_response *data) 193 { 194 struct hdd_ext_scan_context *context; 195 196 hdd_enter(); 197 198 if (wlan_hdd_validate_context(hdd_ctx)) 199 return; 200 if (!data) { 201 hdd_err("data is null"); 202 return; 203 } 204 205 context = &ext_scan_context; 206 207 spin_lock(&context->context_lock); 208 /* validate response received from target*/ 209 if (context->request_id != data->requestId) { 210 spin_unlock(&context->context_lock); 211 hdd_err("Target response id did not match. request_id: %d response_id: %d", 212 context->request_id, data->requestId); 213 return; 214 } 215 216 context->capability_response = *data; 217 complete(&context->response_event); 218 spin_unlock(&context->context_lock); 219 } 220 221 /* 222 * define short names for the global vendor params 223 * used by hdd_extscan_nl_fill_bss() 224 */ 225 #define PARAM_TIME_STAMP \ 226 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP 227 #define PARAM_SSID \ 228 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID 229 #define PARAM_BSSID \ 230 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID 231 #define PARAM_CHANNEL \ 232 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL 233 #define PARAM_RSSI \ 234 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI 235 #define PARAM_RTT \ 236 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT 237 #define PARAM_RTT_SD \ 238 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD 239 #define PARAM_BEACON_PERIOD \ 240 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD 241 #define PARAM_CAPABILITY \ 242 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY 243 #define PARAM_IE_LENGTH \ 244 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH 245 #define PARAM_IE_DATA \ 246 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA 247 248 /** hdd_extscan_nl_fill_bss() - extscan nl fill bss 249 * @skb: socket buffer 250 * @ap: bss information 251 * @idx: nesting index 252 * 253 * Return: 0 on success; error number otherwise 254 */ 255 static int hdd_extscan_nl_fill_bss(struct sk_buff *skb, tSirWifiScanResult *ap, 256 int idx) 257 { 258 struct nlattr *nla_ap; 259 260 nla_ap = nla_nest_start(skb, idx); 261 if (!nla_ap) 262 return -EINVAL; 263 264 if (hdd_wlan_nla_put_u64(skb, PARAM_TIME_STAMP, ap->ts) || 265 nla_put(skb, PARAM_SSID, sizeof(ap->ssid), ap->ssid) || 266 nla_put(skb, PARAM_BSSID, sizeof(ap->bssid), ap->bssid.bytes) || 267 nla_put_u32(skb, PARAM_CHANNEL, ap->channel) || 268 nla_put_s32(skb, PARAM_RSSI, ap->rssi) || 269 nla_put_u32(skb, PARAM_RTT, ap->rtt) || 270 nla_put_u32(skb, PARAM_RTT_SD, ap->rtt_sd) || 271 nla_put_u16(skb, PARAM_BEACON_PERIOD, ap->beaconPeriod) || 272 nla_put_u16(skb, PARAM_CAPABILITY, ap->capability) || 273 nla_put_u16(skb, PARAM_IE_LENGTH, ap->ieLength)) { 274 hdd_err("put fail"); 275 return -EINVAL; 276 } 277 278 if (ap->ieLength) 279 if (nla_put(skb, PARAM_IE_DATA, ap->ieLength, ap->ieData)) { 280 hdd_err("put fail"); 281 return -EINVAL; 282 } 283 284 nla_nest_end(skb, nla_ap); 285 286 return 0; 287 } 288 /* 289 * done with short names for the global vendor params 290 * used by hdd_extscan_nl_fill_bss() 291 */ 292 #undef PARAM_TIME_STAMP 293 #undef PARAM_SSID 294 #undef PARAM_BSSID 295 #undef PARAM_CHANNEL 296 #undef PARAM_RSSI 297 #undef PARAM_RTT 298 #undef PARAM_RTT_SD 299 #undef PARAM_BEACON_PERIOD 300 #undef PARAM_CAPABILITY 301 #undef PARAM_IE_LENGTH 302 #undef PARAM_IE_DATA 303 304 /** 305 * wlan_hdd_cfg80211_extscan_cached_results_ind() - get cached results 306 * @hdd_ctx: hdd global context 307 * @data: cached results 308 * 309 * This function reads the cached results %data, populated the NL 310 * attributes and sends the NL event to the upper layer. 311 * 312 * Return: none 313 */ 314 static void 315 wlan_hdd_cfg80211_extscan_cached_results_ind(struct hdd_context *hdd_ctx, 316 struct extscan_cached_scan_results *data) 317 { 318 struct sk_buff *skb = NULL; 319 struct hdd_ext_scan_context *context; 320 struct extscan_cached_scan_result *result; 321 tSirWifiScanResult *ap; 322 uint32_t i, j, nl_buf_len; 323 bool ignore_cached_results = false; 324 325 /* ENTER() intentionally not used in a frequently invoked API */ 326 327 if (wlan_hdd_validate_context(hdd_ctx)) 328 return; 329 if (!data) { 330 hdd_err("data is null"); 331 return; 332 } 333 334 context = &ext_scan_context; 335 spin_lock(&context->context_lock); 336 ignore_cached_results = context->ignore_cached_results; 337 spin_unlock(&context->context_lock); 338 339 if (ignore_cached_results) { 340 hdd_err("Ignore the cached results received after timeout"); 341 return; 342 } 343 344 #define EXTSCAN_CACHED_NEST_HDRLEN NLA_HDRLEN 345 #define EXTSCAN_CACHED_NL_FIXED_TLV \ 346 ((sizeof(data->request_id) + NLA_HDRLEN) + \ 347 (sizeof(data->num_scan_ids) + NLA_HDRLEN) + \ 348 (sizeof(data->more_data) + NLA_HDRLEN)) 349 #define EXTSCAN_CACHED_NL_SCAN_ID_TLV \ 350 ((sizeof(result->scan_id) + NLA_HDRLEN) + \ 351 (sizeof(result->flags) + NLA_HDRLEN) + \ 352 (sizeof(result->num_results) + NLA_HDRLEN))+ \ 353 (sizeof(result->buckets_scanned) + NLA_HDRLEN) 354 #define EXTSCAN_CACHED_NL_SCAN_RESULTS_TLV \ 355 ((sizeof(ap->ts) + NLA_HDRLEN) + \ 356 (sizeof(ap->ssid) + NLA_HDRLEN) + \ 357 (sizeof(ap->bssid) + NLA_HDRLEN) + \ 358 (sizeof(ap->channel) + NLA_HDRLEN) + \ 359 (sizeof(ap->rssi) + NLA_HDRLEN) + \ 360 (sizeof(ap->rtt) + NLA_HDRLEN) + \ 361 (sizeof(ap->rtt_sd) + NLA_HDRLEN) + \ 362 (sizeof(ap->beaconPeriod) + NLA_HDRLEN) + \ 363 (sizeof(ap->capability) + NLA_HDRLEN) + \ 364 (sizeof(ap->ieLength) + NLA_HDRLEN)) 365 #define EXTSCAN_CACHED_NL_SCAN_RESULTS_IE_DATA_TLV \ 366 (ap->ieLength + NLA_HDRLEN) 367 368 nl_buf_len = NLMSG_HDRLEN; 369 nl_buf_len += EXTSCAN_CACHED_NL_FIXED_TLV; 370 if (data->num_scan_ids) { 371 nl_buf_len += sizeof(result->scan_id) + NLA_HDRLEN; 372 nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; 373 result = &data->result[0]; 374 for (i = 0; i < data->num_scan_ids; i++) { 375 nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; 376 nl_buf_len += EXTSCAN_CACHED_NL_SCAN_ID_TLV; 377 nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; 378 379 ap = &result->ap[0]; 380 for (j = 0; j < result->num_results; j++) { 381 nl_buf_len += EXTSCAN_CACHED_NEST_HDRLEN; 382 nl_buf_len += 383 EXTSCAN_CACHED_NL_SCAN_RESULTS_TLV; 384 if (ap->ieLength) 385 nl_buf_len += 386 EXTSCAN_CACHED_NL_SCAN_RESULTS_IE_DATA_TLV; 387 ap++; 388 } 389 result++; 390 } 391 } 392 393 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); 394 395 if (!skb) { 396 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); 397 goto fail; 398 } 399 hdd_debug("Req Id %u Num_scan_ids %u More Data %u", 400 data->request_id, data->num_scan_ids, data->more_data); 401 402 result = &data->result[0]; 403 for (i = 0; i < data->num_scan_ids; i++) { 404 hdd_debug("[i=%d] scan_id %u flags %u num_results %u buckets scanned %u", 405 i, result->scan_id, result->flags, result->num_results, 406 result->buckets_scanned); 407 408 ap = &result->ap[0]; 409 for (j = 0; j < result->num_results; j++) { 410 /* 411 * Firmware returns timestamp from ext scan start till 412 * BSSID was cached (in micro seconds). Add this with 413 * time gap between system boot up to ext scan start 414 * to derive the time since boot when the 415 * BSSID was cached. 416 */ 417 ap->ts += hdd_ctx->ext_scan_start_since_boot; 418 hdd_debug("Timestamp %llu " 419 "Ssid: %s " 420 "Bssid (" QDF_MAC_ADDR_FMT ") " 421 "Channel %u " 422 "Rssi %d " 423 "RTT %u " 424 "RTT_SD %u " 425 "Beacon Period %u " 426 "Capability 0x%x " 427 "Ie length %d", 428 ap->ts, 429 ap->ssid, 430 QDF_MAC_ADDR_REF(ap->bssid.bytes), 431 ap->channel, 432 ap->rssi, 433 ap->rtt, 434 ap->rtt_sd, 435 ap->beaconPeriod, 436 ap->capability, 437 ap->ieLength); 438 ap++; 439 } 440 result++; 441 } 442 443 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 444 data->request_id) || 445 nla_put_u32(skb, 446 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, 447 data->num_scan_ids) || 448 nla_put_u8(skb, 449 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 450 data->more_data)) { 451 hdd_err("put fail"); 452 goto fail; 453 } 454 455 if (data->num_scan_ids) { 456 struct nlattr *nla_results; 457 458 result = &data->result[0]; 459 460 if (nla_put_u32(skb, 461 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID, 462 result->scan_id)) { 463 hdd_err("put fail"); 464 goto fail; 465 } 466 nla_results = nla_nest_start(skb, 467 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_LIST); 468 if (!nla_results) 469 goto fail; 470 471 for (i = 0; i < data->num_scan_ids; i++) { 472 struct nlattr *nla_result; 473 struct nlattr *nla_aps; 474 475 nla_result = nla_nest_start(skb, i); 476 if (!nla_result) 477 goto fail; 478 479 if (nla_put_u32(skb, 480 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID, 481 result->scan_id) || 482 nla_put_u32(skb, 483 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_FLAGS, 484 result->flags) || 485 nla_put_u32(skb, 486 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED, 487 result->buckets_scanned) || 488 nla_put_u32(skb, 489 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, 490 result->num_results)) { 491 hdd_err("put fail"); 492 goto fail; 493 } 494 495 nla_aps = nla_nest_start(skb, 496 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); 497 if (!nla_aps) 498 goto fail; 499 500 ap = &result->ap[0]; 501 for (j = 0; j < result->num_results; j++) { 502 if (hdd_extscan_nl_fill_bss(skb, ap, j)) 503 goto fail; 504 505 ap++; 506 } 507 nla_nest_end(skb, nla_aps); 508 nla_nest_end(skb, nla_result); 509 result++; 510 } 511 nla_nest_end(skb, nla_results); 512 } 513 514 cfg80211_vendor_cmd_reply(skb); 515 516 if (!data->more_data) { 517 spin_lock(&context->context_lock); 518 context->response_status = 0; 519 complete(&context->response_event); 520 spin_unlock(&context->context_lock); 521 } 522 return; 523 524 fail: 525 if (skb) 526 kfree_skb(skb); 527 528 spin_lock(&context->context_lock); 529 context->response_status = -EINVAL; 530 spin_unlock(&context->context_lock); 531 } 532 533 /** 534 * wlan_hdd_cfg80211_extscan_hotlist_match_ind() - hot list match ind 535 * @hdd_ctx: Pointer to hdd context 536 * @data: Pointer to ext scan result event 537 * 538 * This callback execute in atomic context and must not invoke any 539 * blocking calls. 540 * 541 * Return: none 542 */ 543 static void 544 wlan_hdd_cfg80211_extscan_hotlist_match_ind(struct hdd_context *hdd_ctx, 545 struct extscan_hotlist_match *data) 546 { 547 struct sk_buff *skb = NULL; 548 uint32_t i, index; 549 int flags = cds_get_gfp_flags(); 550 551 hdd_enter(); 552 553 if (wlan_hdd_validate_context(hdd_ctx)) 554 return; 555 if (!data) { 556 hdd_err("data is null"); 557 return; 558 } 559 560 if (data->ap_found) 561 index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX; 562 else 563 index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX; 564 565 skb = cfg80211_vendor_event_alloc( 566 hdd_ctx->wiphy, 567 NULL, 568 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 569 index, flags); 570 571 if (!skb) { 572 hdd_err("cfg80211_vendor_event_alloc failed"); 573 return; 574 } 575 hdd_debug("Req Id: %u Num_APs: %u MoreData: %u ap_found: %u", 576 data->requestId, data->numOfAps, data->moreData, 577 data->ap_found); 578 579 for (i = 0; i < data->numOfAps; i++) { 580 data->ap[i].ts = qdf_get_monotonic_boottime(); 581 582 hdd_debug("[i=%d] Timestamp %llu " 583 "Ssid: %s " 584 "Bssid (" QDF_MAC_ADDR_FMT ") " 585 "Channel %u " 586 "Rssi %d " 587 "RTT %u " 588 "RTT_SD %u", 589 i, 590 data->ap[i].ts, 591 data->ap[i].ssid, 592 QDF_MAC_ADDR_REF(data->ap[i].bssid.bytes), 593 data->ap[i].channel, 594 data->ap[i].rssi, 595 data->ap[i].rtt, data->ap[i].rtt_sd); 596 } 597 598 if (nla_put_u32(skb, 599 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 600 data->requestId) || 601 nla_put_u32(skb, 602 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, 603 data->numOfAps)) { 604 hdd_err("put fail"); 605 goto fail; 606 } 607 608 if (data->numOfAps) { 609 struct nlattr *aps; 610 611 aps = nla_nest_start(skb, 612 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); 613 if (!aps) 614 goto fail; 615 616 for (i = 0; i < data->numOfAps; i++) { 617 struct nlattr *ap; 618 619 ap = nla_nest_start(skb, i); 620 if (!ap) 621 goto fail; 622 623 if (hdd_wlan_nla_put_u64(skb, 624 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, 625 data->ap[i].ts) || 626 nla_put(skb, 627 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID, 628 sizeof(data->ap[i].ssid), 629 data->ap[i].ssid) || 630 nla_put(skb, 631 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID, 632 sizeof(data->ap[i].bssid), 633 data->ap[i].bssid.bytes) || 634 nla_put_u32(skb, 635 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL, 636 data->ap[i].channel) || 637 nla_put_s32(skb, 638 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI, 639 data->ap[i].rssi) || 640 nla_put_u32(skb, 641 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT, 642 data->ap[i].rtt) || 643 nla_put_u32(skb, 644 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD, 645 data->ap[i].rtt_sd)) 646 goto fail; 647 648 nla_nest_end(skb, ap); 649 } 650 nla_nest_end(skb, aps); 651 652 if (nla_put_u8(skb, 653 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 654 data->moreData)) 655 goto fail; 656 } 657 658 cfg80211_vendor_event(skb, flags); 659 hdd_exit(); 660 return; 661 662 fail: 663 kfree_skb(skb); 664 } 665 666 /** 667 * wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind() - 668 * significant wifi change results indication 669 * @hdd_ctx: Pointer to hdd context 670 * @data: Pointer to signif wifi change event 671 * 672 * This callback execute in atomic context and must not invoke any 673 * blocking calls. 674 * 675 * Return: none 676 */ 677 static void 678 wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind( 679 struct hdd_context *hdd_ctx, 680 tpSirWifiSignificantChangeEvent data) 681 { 682 struct sk_buff *skb = NULL; 683 tSirWifiSignificantChange *ap_info; 684 int32_t *rssi; 685 uint32_t i, j; 686 int flags = cds_get_gfp_flags(); 687 688 hdd_enter(); 689 690 if (wlan_hdd_validate_context(hdd_ctx)) 691 return; 692 if (!data) { 693 hdd_err("data is null"); 694 return; 695 } 696 697 skb = cfg80211_vendor_event_alloc( 698 hdd_ctx->wiphy, 699 NULL, 700 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 701 QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX, 702 flags); 703 704 if (!skb) { 705 hdd_err("cfg80211_vendor_event_alloc failed"); 706 return; 707 } 708 hdd_debug("Req Id %u Num results %u More Data %u", 709 data->requestId, data->numResults, data->moreData); 710 711 ap_info = &data->ap[0]; 712 for (i = 0; i < data->numResults; i++) { 713 hdd_debug("[i=%d] " 714 "Bssid (" QDF_MAC_ADDR_FMT ") " 715 "Channel %u " 716 "numOfRssi %d", 717 i, 718 QDF_MAC_ADDR_REF(ap_info->bssid.bytes), 719 ap_info->channel, ap_info->numOfRssi); 720 rssi = &(ap_info)->rssi[0]; 721 for (j = 0; j < ap_info->numOfRssi; j++) 722 hdd_debug("Rssi %d", *rssi++); 723 724 ap_info = (tSirWifiSignificantChange *)((char *)ap_info + 725 ap_info->numOfRssi * sizeof(*rssi) + 726 sizeof(*ap_info)); 727 } 728 729 if (nla_put_u32(skb, 730 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 731 data->requestId) || 732 nla_put_u32(skb, 733 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, 734 data->numResults)) { 735 hdd_err("put fail"); 736 goto fail; 737 } 738 739 if (data->numResults) { 740 struct nlattr *aps; 741 742 aps = nla_nest_start(skb, 743 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); 744 if (!aps) 745 goto fail; 746 747 ap_info = &data->ap[0]; 748 for (i = 0; i < data->numResults; i++) { 749 struct nlattr *ap; 750 751 ap = nla_nest_start(skb, i); 752 if (!ap) 753 goto fail; 754 755 if (nla_put(skb, 756 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID, 757 QDF_MAC_ADDR_SIZE, ap_info->bssid.bytes) || 758 nla_put_u32(skb, 759 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL, 760 ap_info->channel) || 761 nla_put_u32(skb, 762 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI, 763 ap_info->numOfRssi) || 764 nla_put(skb, 765 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST, 766 sizeof(s32) * ap_info->numOfRssi, 767 &(ap_info)->rssi[0])) 768 goto fail; 769 770 nla_nest_end(skb, ap); 771 772 ap_info = (tSirWifiSignificantChange *)((char *)ap_info 773 + ap_info->numOfRssi * sizeof(*rssi) + 774 sizeof(*ap_info)); 775 } 776 nla_nest_end(skb, aps); 777 778 if (nla_put_u8(skb, 779 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 780 data->moreData)) 781 goto fail; 782 } 783 784 cfg80211_vendor_event(skb, flags); 785 return; 786 787 fail: 788 kfree_skb(skb); 789 return; 790 791 } 792 793 /** 794 * wlan_hdd_cfg80211_extscan_full_scan_result_event() - full scan result event 795 * @hdd_ctx: Pointer to hdd context 796 * @data: Pointer to full scan result event 797 * 798 * This callback execute in atomic context and must not invoke any 799 * blocking calls. 800 * 801 * Return: none 802 */ 803 static void 804 wlan_hdd_cfg80211_extscan_full_scan_result_event(struct hdd_context *hdd_ctx, 805 tpSirWifiFullScanResultEvent 806 data) 807 { 808 struct sk_buff *skb; 809 struct hdd_ext_scan_context *context; 810 811 int flags = cds_get_gfp_flags(); 812 813 /* ENTER() intentionally not used in a frequently invoked API */ 814 815 if (wlan_hdd_validate_context(hdd_ctx)) 816 return; 817 if (!data) { 818 hdd_err("data is null"); 819 return; 820 } 821 822 if ((sizeof(*data) + data->ap.ieLength) >= EXTSCAN_EVENT_BUF_SIZE) { 823 hdd_err("Frame exceeded NL size limitation, drop it!!"); 824 return; 825 } 826 skb = cfg80211_vendor_event_alloc( 827 hdd_ctx->wiphy, 828 NULL, 829 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 830 QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX, 831 flags); 832 833 if (!skb) { 834 hdd_err("cfg80211_vendor_event_alloc failed"); 835 return; 836 } 837 838 data->ap.channel = cds_chan_to_freq(data->ap.channel); 839 840 /* 841 * Android does not want the time stamp from the frame. 842 * Instead it wants a monotonic increasing value since boot 843 */ 844 data->ap.ts = qdf_get_monotonic_boottime(); 845 846 hdd_debug("Req Id %u More Data %u", data->requestId, 847 data->moreData); 848 hdd_debug("AP Info: Timestamp %llu Ssid: %s " 849 "Bssid (" QDF_MAC_ADDR_FMT ") " 850 "Channel %u " 851 "Rssi %d " 852 "RTT %u " 853 "RTT_SD %u " 854 "Bcn Period %d " 855 "Capability 0x%X " 856 "IE Length %d", 857 data->ap.ts, 858 data->ap.ssid, 859 QDF_MAC_ADDR_REF(data->ap.bssid.bytes), 860 data->ap.channel, 861 data->ap.rssi, 862 data->ap.rtt, 863 data->ap.rtt_sd, 864 data->ap.beaconPeriod, 865 data->ap.capability, data->ap.ieLength); 866 867 if (nla_put_u32(skb, 868 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 869 data->requestId) || 870 hdd_wlan_nla_put_u64(skb, 871 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, 872 data->ap.ts) || 873 nla_put(skb, 874 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID, 875 sizeof(data->ap.ssid), 876 data->ap.ssid) || 877 nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID, 878 sizeof(data->ap.bssid), 879 data->ap.bssid.bytes) || 880 nla_put_u32(skb, 881 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL, 882 data->ap.channel) || 883 nla_put_s32(skb, 884 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI, 885 data->ap.rssi) || 886 nla_put_u32(skb, 887 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT, 888 data->ap.rtt) || 889 nla_put_u32(skb, 890 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD, 891 data->ap.rtt_sd) || 892 nla_put_u16(skb, 893 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD, 894 data->ap.beaconPeriod) || 895 nla_put_u16(skb, 896 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY, 897 data->ap.capability) || 898 nla_put_u32(skb, 899 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH, 900 data->ap.ieLength) || 901 nla_put_u8(skb, 902 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 903 data->moreData)) { 904 hdd_err("nla put fail"); 905 goto nla_put_failure; 906 } 907 908 if (data->ap.ieLength) { 909 if (nla_put(skb, 910 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA, 911 data->ap.ieLength, data->ap.ieData)) 912 goto nla_put_failure; 913 } 914 915 context = &ext_scan_context; 916 spin_lock(&context->context_lock); 917 if (nla_put_u32(skb, 918 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED, 919 context->buckets_scanned)) { 920 spin_unlock(&context->context_lock); 921 hdd_debug("Failed to include buckets_scanned"); 922 goto nla_put_failure; 923 } 924 spin_unlock(&context->context_lock); 925 926 cfg80211_vendor_event(skb, flags); 927 return; 928 929 nla_put_failure: 930 kfree_skb(skb); 931 } 932 933 /** 934 * wlan_hdd_cfg80211_extscan_scan_res_available_event() - scan result event 935 * @hdd_ctx: Pointer to hdd context 936 * @data: Pointer to scan results available indication param 937 * 938 * This callback execute in atomic context and must not invoke any 939 * blocking calls. 940 * 941 * Return: none 942 */ 943 static void 944 wlan_hdd_cfg80211_extscan_scan_res_available_event( 945 struct hdd_context *hdd_ctx, 946 tpSirExtScanResultsAvailableIndParams data) 947 { 948 struct sk_buff *skb; 949 int flags = cds_get_gfp_flags(); 950 951 hdd_enter(); 952 953 if (wlan_hdd_validate_context(hdd_ctx)) 954 return; 955 if (!data) { 956 hdd_err("data is null"); 957 return; 958 } 959 960 skb = cfg80211_vendor_event_alloc( 961 hdd_ctx->wiphy, 962 NULL, 963 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 964 QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX, 965 flags); 966 967 if (!skb) { 968 hdd_err("cfg80211_vendor_event_alloc failed"); 969 return; 970 } 971 972 hdd_debug("Req Id %u Num results %u", 973 data->requestId, data->numResultsAvailable); 974 if (nla_put_u32(skb, 975 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 976 data->requestId) || 977 nla_put_u32(skb, 978 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, 979 data->numResultsAvailable)) { 980 hdd_err("nla put fail"); 981 goto nla_put_failure; 982 } 983 984 cfg80211_vendor_event(skb, flags); 985 hdd_exit(); 986 return; 987 988 nla_put_failure: 989 kfree_skb(skb); 990 } 991 992 /** 993 * wlan_hdd_cfg80211_extscan_scan_progress_event() - scan progress event 994 * @hdd_ctx: Pointer to hdd context 995 * @data: Pointer to scan event indication param 996 * 997 * This callback execute in atomic context and must not invoke any 998 * blocking calls. 999 * 1000 * Return: none 1001 */ 1002 static void 1003 wlan_hdd_cfg80211_extscan_scan_progress_event(struct hdd_context *hdd_ctx, 1004 tpSirExtScanOnScanEventIndParams 1005 data) 1006 { 1007 struct sk_buff *skb; 1008 int flags = cds_get_gfp_flags(); 1009 struct hdd_ext_scan_context *context; 1010 1011 /* ENTER() intentionally not used in a frequently invoked API */ 1012 1013 if (wlan_hdd_validate_context(hdd_ctx)) 1014 return; 1015 if (!data) { 1016 hdd_err("data is null"); 1017 return; 1018 } 1019 1020 skb = cfg80211_vendor_event_alloc( 1021 hdd_ctx->wiphy, 1022 NULL, 1023 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 1024 QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX, 1025 flags); 1026 1027 if (!skb) { 1028 hdd_err("cfg80211_vendor_event_alloc failed"); 1029 return; 1030 } 1031 1032 hdd_debug("Request Id: %u Scan event type: %u Scan event status: %u buckets scanned: %u", 1033 data->requestId, data->scanEventType, data->status, 1034 data->buckets_scanned); 1035 1036 context = &ext_scan_context; 1037 spin_lock(&context->context_lock); 1038 if (data->scanEventType == WIFI_EXTSCAN_CYCLE_COMPLETED_EVENT) { 1039 context->buckets_scanned = 0; 1040 data->scanEventType = WIFI_EXTSCAN_RESULTS_AVAILABLE; 1041 spin_unlock(&context->context_lock); 1042 } else if (data->scanEventType == WIFI_EXTSCAN_CYCLE_STARTED_EVENT) { 1043 context->buckets_scanned = data->buckets_scanned; 1044 /* No need to report to user space */ 1045 spin_unlock(&context->context_lock); 1046 goto nla_put_failure; 1047 } else { 1048 spin_unlock(&context->context_lock); 1049 } 1050 1051 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 1052 data->requestId) || 1053 nla_put_u8(skb, 1054 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_EVENT_TYPE, 1055 data->scanEventType)) { 1056 hdd_err("nla put fail"); 1057 goto nla_put_failure; 1058 } 1059 1060 cfg80211_vendor_event(skb, flags); 1061 return; 1062 1063 nla_put_failure: 1064 kfree_skb(skb); 1065 } 1066 1067 /** 1068 * wlan_hdd_cfg80211_extscan_epno_match_found() - pno match found 1069 * @hdd_ctx: HDD context 1070 * @data: matched network data 1071 * 1072 * This function reads the matched network data and fills NL vendor attributes 1073 * and send it to upper layer. 1074 * This callback execute in atomic context and must not invoke any 1075 * blocking calls. 1076 * 1077 * Return: 0 on success, error number otherwise 1078 */ 1079 static void 1080 wlan_hdd_cfg80211_extscan_epno_match_found(struct hdd_context *hdd_ctx, 1081 struct pno_match_found *data) 1082 { 1083 struct sk_buff *skb; 1084 uint32_t len, i; 1085 int flags = cds_get_gfp_flags(); 1086 1087 hdd_enter(); 1088 1089 if (wlan_hdd_validate_context(hdd_ctx)) 1090 return; 1091 if (!data) { 1092 hdd_err("data is null"); 1093 return; 1094 } 1095 1096 /* 1097 * If the number of match found APs including IE data exceeds NL 4K size 1098 * limitation, drop that beacon/probe rsp frame. 1099 */ 1100 len = sizeof(*data) + 1101 (data->num_results + sizeof(tSirWifiScanResult)); 1102 for (i = 0; i < data->num_results; i++) 1103 len += data->ap[i].ieLength; 1104 1105 if (len >= EXTSCAN_EVENT_BUF_SIZE) { 1106 hdd_err("Frame exceeded NL size limitation, drop it!"); 1107 return; 1108 } 1109 1110 skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, 1111 NULL, 1112 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 1113 QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND_INDEX, 1114 flags); 1115 1116 if (!skb) { 1117 hdd_err("cfg80211_vendor_event_alloc failed"); 1118 return; 1119 } 1120 1121 hdd_debug("Req Id %u More Data %u num_results %d", 1122 data->request_id, data->more_data, data->num_results); 1123 for (i = 0; i < data->num_results; i++) { 1124 data->ap[i].channel = cds_chan_to_freq(data->ap[i].channel); 1125 hdd_debug("AP Info: Timestamp %llu) Ssid: %s " 1126 "Bssid (" QDF_MAC_ADDR_FMT ") " 1127 "Channel %u " 1128 "Rssi %d " 1129 "RTT %u " 1130 "RTT_SD %u " 1131 "Bcn Period %d " 1132 "Capability 0x%X " 1133 "IE Length %d", 1134 data->ap[i].ts, 1135 data->ap[i].ssid, 1136 QDF_MAC_ADDR_REF(data->ap[i].bssid.bytes), 1137 data->ap[i].channel, 1138 data->ap[i].rssi, 1139 data->ap[i].rtt, 1140 data->ap[i].rtt_sd, 1141 data->ap[i].beaconPeriod, 1142 data->ap[i].capability, 1143 data->ap[i].ieLength); 1144 } 1145 1146 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 1147 data->request_id) || 1148 nla_put_u32(skb, 1149 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE, 1150 data->num_results) || 1151 nla_put_u8(skb, 1152 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 1153 data->more_data)) { 1154 hdd_err("nla put fail"); 1155 goto fail; 1156 } 1157 1158 if (data->num_results) { 1159 struct nlattr *nla_aps; 1160 1161 nla_aps = nla_nest_start(skb, 1162 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); 1163 if (!nla_aps) 1164 goto fail; 1165 1166 for (i = 0; i < data->num_results; i++) { 1167 if (hdd_extscan_nl_fill_bss(skb, &data->ap[i], i)) 1168 goto fail; 1169 } 1170 nla_nest_end(skb, nla_aps); 1171 } 1172 1173 cfg80211_vendor_event(skb, flags); 1174 return; 1175 1176 fail: 1177 kfree_skb(skb); 1178 } 1179 1180 /** 1181 * wlan_hdd_cfg80211_passpoint_match_found() - passpoint match found 1182 * @hddctx: HDD context 1183 * @data: matched network data 1184 * 1185 * This function reads the match network %data and fill in the skb with 1186 * NL attributes and send up the NL event 1187 * This callback execute in atomic context and must not invoke any 1188 * blocking calls. 1189 * 1190 * Return: none 1191 */ 1192 static void 1193 wlan_hdd_cfg80211_passpoint_match_found(void *ctx, 1194 struct wifi_passpoint_match *data) 1195 { 1196 struct hdd_context *hdd_ctx = ctx; 1197 struct sk_buff *skb = NULL; 1198 uint32_t len, i, num_matches = 1, more_data = 0; 1199 struct nlattr *nla_aps, *nla_bss; 1200 int flags = cds_get_gfp_flags(); 1201 1202 hdd_enter(); 1203 1204 if (wlan_hdd_validate_context(hdd_ctx)) 1205 return; 1206 if (!data) { 1207 hdd_err("data is null"); 1208 return; 1209 } 1210 1211 len = sizeof(*data) + data->ap.ieLength + data->anqp_len; 1212 if (len >= EXTSCAN_EVENT_BUF_SIZE) { 1213 hdd_err("Result exceeded NL size limitation, drop it"); 1214 return; 1215 } 1216 1217 skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, 1218 NULL, 1219 EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, 1220 QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND_INDEX, 1221 flags); 1222 1223 if (!skb) { 1224 hdd_err("cfg80211_vendor_event_alloc failed"); 1225 return; 1226 } 1227 1228 hdd_debug("Req Id %u Id %u ANQP length %u num_matches %u", 1229 data->request_id, data->id, data->anqp_len, num_matches); 1230 for (i = 0; i < num_matches; i++) { 1231 hdd_debug("AP Info: Timestamp %llu Ssid: %s " 1232 "Bssid (" QDF_MAC_ADDR_FMT ") " 1233 "Channel %u " 1234 "Rssi %d " 1235 "RTT %u " 1236 "RTT_SD %u " 1237 "Bcn Period %d " 1238 "Capability 0x%X " 1239 "IE Length %d", 1240 data->ap.ts, 1241 data->ap.ssid, 1242 QDF_MAC_ADDR_REF(data->ap.bssid.bytes), 1243 data->ap.channel, 1244 data->ap.rssi, 1245 data->ap.rtt, 1246 data->ap.rtt_sd, 1247 data->ap.beaconPeriod, 1248 data->ap.capability, 1249 data->ap.ieLength); 1250 } 1251 1252 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID, 1253 data->request_id) || 1254 nla_put_u32(skb, 1255 QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES, 1256 num_matches) || 1257 nla_put_u8(skb, 1258 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 1259 more_data)) { 1260 hdd_err("nla put fail"); 1261 goto fail; 1262 } 1263 1264 nla_aps = nla_nest_start(skb, 1265 QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST); 1266 if (!nla_aps) 1267 goto fail; 1268 1269 for (i = 0; i < num_matches; i++) { 1270 struct nlattr *nla_ap; 1271 1272 nla_ap = nla_nest_start(skb, i); 1273 if (!nla_ap) 1274 goto fail; 1275 1276 if (nla_put_u32(skb, 1277 QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID, 1278 data->id) || 1279 nla_put_u32(skb, 1280 QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN, 1281 data->anqp_len)) { 1282 goto fail; 1283 } 1284 1285 if (data->anqp_len) 1286 if (nla_put(skb, 1287 QCA_WLAN_VENDOR_ATTR_EXTSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP, 1288 data->anqp_len, data->anqp)) 1289 goto fail; 1290 1291 nla_bss = nla_nest_start(skb, 1292 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST); 1293 if (!nla_bss) 1294 goto fail; 1295 1296 if (hdd_extscan_nl_fill_bss(skb, &data->ap, 0)) 1297 goto fail; 1298 1299 nla_nest_end(skb, nla_bss); 1300 nla_nest_end(skb, nla_ap); 1301 } 1302 nla_nest_end(skb, nla_aps); 1303 1304 cfg80211_vendor_event(skb, flags); 1305 return; 1306 1307 fail: 1308 kfree_skb(skb); 1309 } 1310 1311 /** 1312 * wlan_hdd_cfg80211_extscan_generic_rsp() - 1313 * Handle a generic ExtScan Response message 1314 * @ctx: HDD context registered with SME 1315 * @response: The ExtScan response from firmware 1316 * 1317 * This function will handle a generic ExtScan response message from 1318 * firmware and will communicate the result to the userspace thread 1319 * that is waiting for the response. 1320 * 1321 * Return: none 1322 */ 1323 static void 1324 wlan_hdd_cfg80211_extscan_generic_rsp(struct hdd_context *hdd_ctx, 1325 struct sir_extscan_generic_response *response) 1326 { 1327 struct hdd_ext_scan_context *context; 1328 1329 hdd_enter(); 1330 1331 if (wlan_hdd_validate_context(hdd_ctx) || !response) { 1332 hdd_err("HDD context is not valid or response(%pK) is null", 1333 response); 1334 return; 1335 } 1336 1337 hdd_debug("request %u status %u", 1338 response->request_id, response->status); 1339 1340 context = &ext_scan_context; 1341 spin_lock(&context->context_lock); 1342 if (context->request_id == response->request_id) { 1343 context->response_status = response->status ? -EINVAL : 0; 1344 complete(&context->response_event); 1345 } 1346 spin_unlock(&context->context_lock); 1347 } 1348 1349 void wlan_hdd_cfg80211_extscan_callback(hdd_handle_t hdd_handle, 1350 const uint16_t event_id, void *msg) 1351 { 1352 struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle); 1353 1354 /* ENTER() intentionally not used in a frequently invoked API */ 1355 1356 if (wlan_hdd_validate_context(hdd_ctx)) 1357 return; 1358 1359 hdd_debug("Rcvd Event %d", event_id); 1360 1361 switch (event_id) { 1362 case eSIR_EXTSCAN_CACHED_RESULTS_RSP: 1363 /* There is no need to send this response to upper layer 1364 * Just log the message 1365 */ 1366 hdd_debug("Rcvd eSIR_EXTSCAN_CACHED_RESULTS_RSP"); 1367 break; 1368 1369 case eSIR_EXTSCAN_GET_CAPABILITIES_IND: 1370 wlan_hdd_cfg80211_extscan_get_capabilities_rsp(hdd_ctx, msg); 1371 break; 1372 1373 case eSIR_EXTSCAN_HOTLIST_MATCH_IND: 1374 wlan_hdd_cfg80211_extscan_hotlist_match_ind(hdd_ctx, msg); 1375 break; 1376 1377 case eSIR_EXTSCAN_SIGNIFICANT_WIFI_CHANGE_RESULTS_IND: 1378 wlan_hdd_cfg80211_extscan_signif_wifi_change_results_ind(hdd_ctx, 1379 msg); 1380 break; 1381 1382 case eSIR_EXTSCAN_CACHED_RESULTS_IND: 1383 wlan_hdd_cfg80211_extscan_cached_results_ind(hdd_ctx, msg); 1384 break; 1385 1386 case eSIR_EXTSCAN_SCAN_RES_AVAILABLE_IND: 1387 wlan_hdd_cfg80211_extscan_scan_res_available_event(hdd_ctx, 1388 msg); 1389 break; 1390 1391 case eSIR_EXTSCAN_FULL_SCAN_RESULT_IND: 1392 wlan_hdd_cfg80211_extscan_full_scan_result_event(hdd_ctx, msg); 1393 break; 1394 1395 case eSIR_EPNO_NETWORK_FOUND_IND: 1396 wlan_hdd_cfg80211_extscan_epno_match_found(hdd_ctx, msg); 1397 break; 1398 1399 case eSIR_EXTSCAN_SCAN_PROGRESS_EVENT_IND: 1400 wlan_hdd_cfg80211_extscan_scan_progress_event(hdd_ctx, msg); 1401 break; 1402 1403 case eSIR_PASSPOINT_NETWORK_FOUND_IND: 1404 wlan_hdd_cfg80211_passpoint_match_found(hdd_ctx, msg); 1405 break; 1406 1407 case eSIR_EXTSCAN_START_RSP: 1408 case eSIR_EXTSCAN_STOP_RSP: 1409 case eSIR_EXTSCAN_SET_BSSID_HOTLIST_RSP: 1410 case eSIR_EXTSCAN_RESET_BSSID_HOTLIST_RSP: 1411 case eSIR_EXTSCAN_SET_SIGNIFICANT_WIFI_CHANGE_RSP: 1412 case eSIR_EXTSCAN_RESET_SIGNIFICANT_WIFI_CHANGE_RSP: 1413 case eSIR_EXTSCAN_SET_SSID_HOTLIST_RSP: 1414 case eSIR_EXTSCAN_RESET_SSID_HOTLIST_RSP: 1415 wlan_hdd_cfg80211_extscan_generic_rsp(hdd_ctx, msg); 1416 break; 1417 1418 default: 1419 hdd_err("Unknown event type: %u", event_id); 1420 break; 1421 } 1422 } 1423 1424 /* 1425 * define short names for the global vendor params 1426 * used by wlan_hdd_send_ext_scan_capability() 1427 */ 1428 #define PARAM_REQUEST_ID \ 1429 QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID 1430 #define PARAM_STATUS \ 1431 QCA_WLAN_VENDOR_ATTR_EXTSCAN_STATUS 1432 #define MAX_EXTSCAN_CACHE_SIZE \ 1433 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE 1434 #define MAX_SCAN_BUCKETS \ 1435 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS 1436 #define MAX_AP_CACHE_PER_SCAN \ 1437 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN 1438 #define MAX_RSSI_SAMPLE_SIZE \ 1439 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE 1440 #define MAX_SCAN_RPT_THRHOLD \ 1441 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD 1442 #define MAX_HOTLIST_BSSIDS \ 1443 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS 1444 #define MAX_SIGNIFICANT_WIFI_CHANGE_APS \ 1445 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS 1446 #define MAX_BSSID_HISTORY_ENTRIES \ 1447 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES 1448 #define MAX_HOTLIST_SSIDS \ 1449 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS 1450 #define MAX_NUM_EPNO_NETS \ 1451 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS 1452 #define MAX_NUM_EPNO_NETS_BY_SSID \ 1453 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID 1454 #define MAX_NUM_WHITELISTED_SSID \ 1455 QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID 1456 #define MAX_NUM_BLACKLISTED_BSSID \ 1457 QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID 1458 /** 1459 * wlan_hdd_send_ext_scan_capability - send ext scan capability to user space 1460 * @hdd_ctx: Pointer to hdd context 1461 * 1462 * Return: 0 for success, non-zero for failure 1463 */ 1464 static int wlan_hdd_send_ext_scan_capability(struct hdd_context *hdd_ctx) 1465 { 1466 int ret; 1467 struct sk_buff *skb; 1468 struct ext_scan_capabilities_response *data; 1469 uint32_t nl_buf_len; 1470 1471 ret = wlan_hdd_validate_context(hdd_ctx); 1472 if (0 != ret) 1473 return ret; 1474 1475 data = &(ext_scan_context.capability_response); 1476 1477 nl_buf_len = NLMSG_HDRLEN; 1478 nl_buf_len += (sizeof(data->requestId) + NLA_HDRLEN) + 1479 (sizeof(data->status) + NLA_HDRLEN) + 1480 (sizeof(data->max_scan_cache_size) + NLA_HDRLEN) + 1481 (sizeof(data->max_scan_buckets) + NLA_HDRLEN) + 1482 (sizeof(data->max_ap_cache_per_scan) + NLA_HDRLEN) + 1483 (sizeof(data->max_rssi_sample_size) + NLA_HDRLEN) + 1484 (sizeof(data->max_scan_reporting_threshold) + NLA_HDRLEN) + 1485 (sizeof(data->max_hotlist_bssids) + NLA_HDRLEN) + 1486 (sizeof(data->max_significant_wifi_change_aps) + NLA_HDRLEN) + 1487 (sizeof(data->max_bssid_history_entries) + NLA_HDRLEN) + 1488 (sizeof(data->max_hotlist_ssids) + NLA_HDRLEN) + 1489 (sizeof(data->max_number_epno_networks) + NLA_HDRLEN) + 1490 (sizeof(data->max_number_epno_networks_by_ssid) + NLA_HDRLEN) + 1491 (sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN) + 1492 (sizeof(data->max_number_of_black_listed_bssid) + NLA_HDRLEN); 1493 1494 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); 1495 1496 if (!skb) { 1497 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); 1498 return -ENOMEM; 1499 } 1500 1501 1502 hdd_debug("Req Id %u", data->requestId); 1503 hdd_debug("Status %u", data->status); 1504 hdd_debug("Scan cache size %u", 1505 data->max_scan_cache_size); 1506 hdd_debug("Scan buckets %u", data->max_scan_buckets); 1507 hdd_debug("Max AP per scan %u", 1508 data->max_ap_cache_per_scan); 1509 hdd_debug("max_rssi_sample_size %u", 1510 data->max_rssi_sample_size); 1511 hdd_debug("max_scan_reporting_threshold %u", 1512 data->max_scan_reporting_threshold); 1513 hdd_debug("max_hotlist_bssids %u", 1514 data->max_hotlist_bssids); 1515 hdd_debug("max_significant_wifi_change_aps %u", 1516 data->max_significant_wifi_change_aps); 1517 hdd_debug("max_bssid_history_entries %u", 1518 data->max_bssid_history_entries); 1519 hdd_debug("max_hotlist_ssids %u", data->max_hotlist_ssids); 1520 hdd_debug("max_number_epno_networks %u", 1521 data->max_number_epno_networks); 1522 hdd_debug("max_number_epno_networks_by_ssid %u", 1523 data->max_number_epno_networks_by_ssid); 1524 hdd_debug("max_number_of_white_listed_ssid %u", 1525 data->max_number_of_white_listed_ssid); 1526 hdd_debug("max_number_of_black_listed_bssid (%u)", 1527 data->max_number_of_black_listed_bssid); 1528 1529 if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) || 1530 nla_put_u32(skb, PARAM_STATUS, data->status) || 1531 nla_put_u32(skb, MAX_EXTSCAN_CACHE_SIZE, 1532 data->max_scan_cache_size) || 1533 nla_put_u32(skb, MAX_SCAN_BUCKETS, data->max_scan_buckets) || 1534 nla_put_u32(skb, MAX_AP_CACHE_PER_SCAN, 1535 data->max_ap_cache_per_scan) || 1536 nla_put_u32(skb, MAX_RSSI_SAMPLE_SIZE, 1537 data->max_rssi_sample_size) || 1538 nla_put_u32(skb, MAX_SCAN_RPT_THRHOLD, 1539 data->max_scan_reporting_threshold) || 1540 nla_put_u32(skb, MAX_HOTLIST_BSSIDS, data->max_hotlist_bssids) || 1541 nla_put_u32(skb, MAX_SIGNIFICANT_WIFI_CHANGE_APS, 1542 data->max_significant_wifi_change_aps) || 1543 nla_put_u32(skb, MAX_BSSID_HISTORY_ENTRIES, 1544 data->max_bssid_history_entries) || 1545 nla_put_u32(skb, MAX_HOTLIST_SSIDS, data->max_hotlist_ssids) || 1546 nla_put_u32(skb, MAX_NUM_EPNO_NETS, 1547 data->max_number_epno_networks) || 1548 nla_put_u32(skb, MAX_NUM_EPNO_NETS_BY_SSID, 1549 data->max_number_epno_networks_by_ssid) || 1550 nla_put_u32(skb, MAX_NUM_WHITELISTED_SSID, 1551 data->max_number_of_white_listed_ssid) || 1552 nla_put_u32(skb, MAX_NUM_BLACKLISTED_BSSID, 1553 data->max_number_of_black_listed_bssid)) { 1554 hdd_err("nla put fail"); 1555 goto nla_put_failure; 1556 } 1557 1558 cfg80211_vendor_cmd_reply(skb); 1559 return 0; 1560 1561 nla_put_failure: 1562 kfree_skb(skb); 1563 return -EINVAL; 1564 } 1565 /* 1566 * done with short names for the global vendor params 1567 * used by wlan_hdd_send_ext_scan_capability() 1568 */ 1569 #undef PARAM_REQUEST_ID 1570 #undef PARAM_STATUS 1571 #undef MAX_EXTSCAN_CACHE_SIZE 1572 #undef MAX_SCAN_BUCKETS 1573 #undef MAX_AP_CACHE_PER_SCAN 1574 #undef MAX_RSSI_SAMPLE_SIZE 1575 #undef MAX_SCAN_RPT_THRHOLD 1576 #undef MAX_HOTLIST_BSSIDS 1577 #undef MAX_SIGNIFICANT_WIFI_CHANGE_APS 1578 #undef MAX_BSSID_HISTORY_ENTRIES 1579 #undef MAX_HOTLIST_SSIDS 1580 #undef MAX_NUM_EPNO_NETS 1581 #undef MAX_NUM_EPNO_NETS_BY_SSID 1582 #undef MAX_NUM_WHITELISTED_SSID 1583 #undef MAX_NUM_BLACKLISTED_BSSID 1584 1585 /** 1586 * __wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities 1587 * @wiphy: Pointer to wireless phy 1588 * @wdev: Pointer to wireless device 1589 * @data: Pointer to data 1590 * @data_len: Data length 1591 * 1592 * Return: none 1593 */ 1594 static int 1595 __wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, 1596 struct wireless_dev *wdev, 1597 const void *data, int data_len) 1598 { 1599 int id, ret; 1600 unsigned long rc; 1601 struct hdd_ext_scan_context *context; 1602 struct extscan_capabilities_params params; 1603 struct net_device *dev = wdev->netdev; 1604 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 1605 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 1606 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 1607 QDF_STATUS status; 1608 1609 hdd_enter_dev(dev); 1610 1611 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 1612 hdd_err("Command not allowed in FTM mode"); 1613 return -EPERM; 1614 } 1615 1616 ret = wlan_hdd_validate_context(hdd_ctx); 1617 if (0 != ret) 1618 return -EINVAL; 1619 1620 if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) { 1621 hdd_err("Driver Modules are closed"); 1622 return -EINVAL; 1623 } 1624 1625 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 1626 hdd_err("extscan not supported"); 1627 return -ENOTSUPP; 1628 } 1629 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, 1630 wlan_hdd_extscan_config_policy)) { 1631 hdd_err("Invalid ATTR"); 1632 return -EINVAL; 1633 } 1634 1635 /* Parse and fetch request Id */ 1636 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 1637 if (!tb[id]) { 1638 hdd_err("attr request id failed"); 1639 return -EINVAL; 1640 } 1641 1642 params.request_id = nla_get_u32(tb[id]); 1643 params.vdev_id = adapter->vdev_id; 1644 hdd_debug("Req Id %d Vdev Id %d", params.request_id, params.vdev_id); 1645 1646 context = &ext_scan_context; 1647 spin_lock(&context->context_lock); 1648 context->request_id = params.request_id; 1649 INIT_COMPLETION(context->response_event); 1650 spin_unlock(&context->context_lock); 1651 1652 status = sme_ext_scan_get_capabilities(hdd_ctx->mac_handle, ¶ms); 1653 if (!QDF_IS_STATUS_SUCCESS(status)) { 1654 hdd_err("sme_ext_scan_get_capabilities failed(err=%d)", 1655 status); 1656 return -EINVAL; 1657 } 1658 1659 rc = wait_for_completion_timeout(&context->response_event, 1660 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 1661 if (!rc) { 1662 hdd_err("Target response timed out"); 1663 return -ETIMEDOUT; 1664 } 1665 1666 ret = wlan_hdd_send_ext_scan_capability(hdd_ctx); 1667 if (ret) 1668 hdd_err("Failed to send ext scan capability to user space"); 1669 hdd_exit(); 1670 return ret; 1671 } 1672 1673 /** 1674 * wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities 1675 * @wiphy: Pointer to wiphy 1676 * @wdev: Pointer to wdev 1677 * @data: Pointer to data 1678 * @data_len: Data length 1679 * 1680 * Return: 0 for success, non-zero for failure 1681 */ 1682 int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, 1683 struct wireless_dev *wdev, 1684 const void *data, int data_len) 1685 { 1686 int errno; 1687 struct osif_vdev_sync *vdev_sync; 1688 1689 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 1690 if (errno) 1691 return errno; 1692 1693 errno = __wlan_hdd_cfg80211_extscan_get_capabilities(wiphy, wdev, 1694 data, data_len); 1695 1696 osif_vdev_sync_op_stop(vdev_sync); 1697 1698 return errno; 1699 } 1700 1701 /** 1702 * __wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results 1703 * @wiphy: wiphy pointer 1704 * @wdev: pointer to struct wireless_dev 1705 * @data: pointer to incoming NL vendor data 1706 * @data_len: length of @data 1707 * 1708 * This function parses the incoming NL vendor command data attributes and 1709 * invokes the SME Api and blocks on a completion variable. 1710 * Each WMI event with cached scan results data chunk results in 1711 * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each 1712 * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. 1713 * 1714 * If timeout happens before receiving all of the data, this function sets 1715 * a context variable @ignore_cached_results to %true, all of the next data 1716 * chunks are checked against this variable and dropped. 1717 * 1718 * Return: 0 on success; error number otherwise. 1719 */ 1720 static int 1721 __wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, 1722 struct wireless_dev *wdev, 1723 const void *data, 1724 int data_len) 1725 { 1726 struct extscan_cached_result_params params; 1727 struct net_device *dev = wdev->netdev; 1728 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 1729 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 1730 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 1731 struct hdd_ext_scan_context *context; 1732 QDF_STATUS status; 1733 int id, retval; 1734 unsigned long rc; 1735 1736 /* ENTER_DEV() intentionally not used in a frequently invoked API */ 1737 1738 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 1739 hdd_err("Command not allowed in FTM mode"); 1740 return -EPERM; 1741 } 1742 1743 retval = wlan_hdd_validate_context(hdd_ctx); 1744 if (0 != retval) 1745 return -EINVAL; 1746 1747 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 1748 hdd_err("extscan not supported"); 1749 return -ENOTSUPP; 1750 } 1751 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, 1752 wlan_hdd_extscan_config_policy)) { 1753 hdd_err("Invalid ATTR"); 1754 return -EINVAL; 1755 } 1756 1757 /* Parse and fetch request Id */ 1758 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 1759 if (!tb[id]) { 1760 hdd_err("attr request id failed"); 1761 return -EINVAL; 1762 } 1763 1764 params.request_id = nla_get_u32(tb[id]); 1765 params.vdev_id = adapter->vdev_id; 1766 1767 /* Parse and fetch flush parameter */ 1768 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH; 1769 if (!tb[id]) { 1770 hdd_err("attr flush failed"); 1771 return -EINVAL; 1772 } 1773 params.flush = nla_get_u8(tb[id]); 1774 hdd_debug("Req Id: %u Vdev Id: %d Flush: %d", 1775 params.request_id, params.vdev_id, params.flush); 1776 1777 context = &ext_scan_context; 1778 spin_lock(&context->context_lock); 1779 context->request_id = params.request_id; 1780 context->ignore_cached_results = false; 1781 INIT_COMPLETION(context->response_event); 1782 spin_unlock(&context->context_lock); 1783 1784 status = sme_get_cached_results(hdd_ctx->mac_handle, ¶ms); 1785 if (!QDF_IS_STATUS_SUCCESS(status)) { 1786 hdd_err("sme_get_cached_results failed(err=%d)", status); 1787 return -EINVAL; 1788 } 1789 1790 rc = wait_for_completion_timeout(&context->response_event, 1791 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 1792 if (!rc) { 1793 hdd_err("Target response timed out"); 1794 retval = -ETIMEDOUT; 1795 spin_lock(&context->context_lock); 1796 context->ignore_cached_results = true; 1797 spin_unlock(&context->context_lock); 1798 } else { 1799 spin_lock(&context->context_lock); 1800 retval = context->response_status; 1801 spin_unlock(&context->context_lock); 1802 } 1803 return retval; 1804 } 1805 1806 /** 1807 * wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results 1808 * @wiphy: wiphy pointer 1809 * @wdev: pointer to struct wireless_dev 1810 * @data: pointer to incoming NL vendor data 1811 * @data_len: length of @data 1812 * 1813 * This function parses the incoming NL vendor command data attributes and 1814 * invokes the SME Api and blocks on a completion variable. 1815 * Each WMI event with cached scan results data chunk results in 1816 * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each 1817 * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. 1818 * 1819 * If timeout happens before receiving all of the data, this function sets 1820 * a context variable @ignore_cached_results to %true, all of the next data 1821 * chunks are checked against this variable and dropped. 1822 * 1823 * Return: 0 on success; error number otherwise. 1824 */ 1825 int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, 1826 struct wireless_dev *wdev, 1827 const void *data, int data_len) 1828 { 1829 int errno; 1830 struct osif_vdev_sync *vdev_sync; 1831 1832 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 1833 if (errno) 1834 return errno; 1835 1836 errno = __wlan_hdd_cfg80211_extscan_get_cached_results(wiphy, wdev, 1837 data, data_len); 1838 1839 osif_vdev_sync_op_stop(vdev_sync); 1840 1841 return errno; 1842 } 1843 1844 /** 1845 * hdd_parse_ap_rssi_threshold() - parse AP RSSI threshold parameters 1846 * @attr: netlink attribute containing the AP RSSI threshold parameters 1847 * @ap: destination buffer for the parsed parameters 1848 * 1849 * This function parses the BSSID, low RSSI and high RSSI values from 1850 * the @attr netlink attribute, storing the parsed values in @ap. 1851 * 1852 * Return: 0 if @attr is parsed and all required attributes are 1853 * present, otherwise a negative errno. 1854 */ 1855 static int hdd_parse_ap_rssi_threshold(struct nlattr *attr, 1856 struct ap_threshold_params *ap) 1857 { 1858 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 1859 int id; 1860 1861 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, 1862 nla_data(attr), nla_len(attr), 1863 wlan_hdd_extscan_config_policy)) { 1864 hdd_err("nla_parse failed"); 1865 return -EINVAL; 1866 } 1867 1868 /* Parse and fetch MAC address */ 1869 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID; 1870 if (!tb[id]) { 1871 hdd_err("attr mac address failed"); 1872 return -EINVAL; 1873 } 1874 nla_memcpy(ap->bssid.bytes, tb[id], QDF_MAC_ADDR_SIZE); 1875 hdd_debug("BSSID: " QDF_MAC_ADDR_FMT, 1876 QDF_MAC_ADDR_REF(ap->bssid.bytes)); 1877 1878 /* Parse and fetch low RSSI */ 1879 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW; 1880 if (!tb[id]) { 1881 hdd_err("attr low RSSI failed"); 1882 return -EINVAL; 1883 } 1884 ap->low = nla_get_s32(tb[id]); 1885 hdd_debug("RSSI low %d", ap->low); 1886 1887 /* Parse and fetch high RSSI */ 1888 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH; 1889 if (!tb[id]) { 1890 hdd_err("attr high RSSI failed"); 1891 return -EINVAL; 1892 } 1893 ap->high = nla_get_s32(tb[id]); 1894 hdd_debug("RSSI High %d", ap->high); 1895 1896 return 0; 1897 } 1898 1899 /** 1900 * __wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set bssid hot list 1901 * @wiphy: Pointer to wireless phy 1902 * @wdev: Pointer to wireless device 1903 * @data: Pointer to data 1904 * @data_len: Data length 1905 * 1906 * Return: none 1907 */ 1908 static int 1909 __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, 1910 struct wireless_dev *wdev, 1911 const void *data, 1912 int data_len) 1913 { 1914 struct extscan_bssid_hotlist_set_params *params; 1915 struct net_device *dev = wdev->netdev; 1916 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 1917 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 1918 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 1919 struct nlattr *apth; 1920 struct hdd_ext_scan_context *context; 1921 QDF_STATUS status; 1922 uint8_t i; 1923 int id, rem, retval; 1924 unsigned long rc; 1925 1926 hdd_enter_dev(dev); 1927 1928 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 1929 hdd_err("Command not allowed in FTM mode"); 1930 return -EPERM; 1931 } 1932 1933 retval = wlan_hdd_validate_context(hdd_ctx); 1934 if (0 != retval) 1935 return -EINVAL; 1936 1937 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 1938 hdd_err("extscan not supported"); 1939 return -ENOTSUPP; 1940 } 1941 1942 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, 1943 data, data_len, 1944 wlan_hdd_extscan_config_policy)) { 1945 hdd_err("Invalid ATTR"); 1946 return -EINVAL; 1947 } 1948 1949 params = qdf_mem_malloc(sizeof(*params)); 1950 if (!params) 1951 return -ENOMEM; 1952 1953 /* assume the worst until proven otherwise */ 1954 retval = -EINVAL; 1955 1956 /* Parse and fetch request Id */ 1957 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 1958 if (!tb[id]) { 1959 hdd_err("attr request id failed"); 1960 goto fail; 1961 } 1962 1963 params->request_id = nla_get_u32(tb[id]); 1964 hdd_debug("Req Id %d", params->request_id); 1965 1966 /* Parse and fetch number of APs */ 1967 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP; 1968 if (!tb[id]) { 1969 hdd_err("attr number of AP failed"); 1970 goto fail; 1971 } 1972 1973 params->num_ap = nla_get_u32(tb[id]); 1974 if (params->num_ap > WMI_WLAN_EXTSCAN_MAX_HOTLIST_APS) { 1975 hdd_err("Number of AP: %u exceeds max: %u", 1976 params->num_ap, WMI_WLAN_EXTSCAN_MAX_HOTLIST_APS); 1977 goto fail; 1978 } 1979 params->vdev_id = adapter->vdev_id; 1980 hdd_debug("Number of AP %d vdev Id %d", 1981 params->num_ap, params->vdev_id); 1982 1983 /* Parse and fetch lost ap sample size */ 1984 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE; 1985 if (!tb[id]) { 1986 hdd_err("attr lost ap sample size failed"); 1987 goto fail; 1988 } 1989 1990 params->lost_ap_sample_size = nla_get_u32(tb[id]); 1991 hdd_debug("Lost ap sample size %d", 1992 params->lost_ap_sample_size); 1993 1994 /* Parse the AP Threshold array */ 1995 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM; 1996 if (!tb[id]) { 1997 hdd_err("attr ap threshold failed"); 1998 goto fail; 1999 } 2000 2001 i = 0; 2002 nla_for_each_nested(apth, tb[id], rem) { 2003 if (i == params->num_ap) { 2004 hdd_warn("Ignoring excess AP"); 2005 break; 2006 } 2007 2008 retval = hdd_parse_ap_rssi_threshold(apth, ¶ms->ap[i]); 2009 if (retval) 2010 goto fail; 2011 2012 i++; 2013 } 2014 2015 if (i < params->num_ap) { 2016 hdd_warn("Number of AP %u less than expected %u", 2017 i, params->num_ap); 2018 params->num_ap = i; 2019 } 2020 2021 context = &ext_scan_context; 2022 spin_lock(&context->context_lock); 2023 INIT_COMPLETION(context->response_event); 2024 context->request_id = params->request_id; 2025 spin_unlock(&context->context_lock); 2026 2027 status = sme_set_bss_hotlist(hdd_ctx->mac_handle, params); 2028 if (!QDF_IS_STATUS_SUCCESS(status)) { 2029 hdd_err("sme_set_bss_hotlist failed(err=%d)", status); 2030 retval = qdf_status_to_os_return(status); 2031 goto fail; 2032 } 2033 2034 /* request was sent -- wait for the response */ 2035 rc = wait_for_completion_timeout 2036 (&context->response_event, 2037 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 2038 2039 if (!rc) { 2040 hdd_err("sme_set_bss_hotlist timed out"); 2041 retval = -ETIMEDOUT; 2042 } else { 2043 spin_lock(&context->context_lock); 2044 if (context->request_id == params->request_id) 2045 retval = context->response_status; 2046 else 2047 retval = -EINVAL; 2048 spin_unlock(&context->context_lock); 2049 } 2050 hdd_exit(); 2051 2052 fail: 2053 qdf_mem_free(params); 2054 return retval; 2055 } 2056 2057 /** 2058 * wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set ext scan bssid hotlist 2059 * @wiphy: Pointer to wiphy 2060 * @wdev: Pointer to wdev 2061 * @data: Pointer to data 2062 * @data_len: Data length 2063 * 2064 * Return: 0 for success, non-zero for failure 2065 */ 2066 int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, 2067 struct wireless_dev *wdev, 2068 const void *data, int data_len) 2069 { 2070 int errno; 2071 struct osif_vdev_sync *vdev_sync; 2072 2073 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 2074 if (errno) 2075 return errno; 2076 2077 errno = __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(wiphy, wdev, 2078 data, data_len); 2079 2080 osif_vdev_sync_op_stop(vdev_sync); 2081 2082 return errno; 2083 } 2084 2085 2086 /** 2087 * __wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change 2088 * @wiphy: Pointer to wireless phy 2089 * @wdev: Pointer to wireless device 2090 * @data: Pointer to data 2091 * @data_len: Data length 2092 * 2093 * Return: none 2094 */ 2095 static int 2096 __wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, 2097 struct wireless_dev *wdev, 2098 const void *data, 2099 int data_len) 2100 { 2101 struct extscan_set_sig_changereq_params *params; 2102 struct net_device *dev = wdev->netdev; 2103 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 2104 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 2105 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 2106 struct nlattr *apth; 2107 struct hdd_ext_scan_context *context; 2108 QDF_STATUS status; 2109 uint8_t i; 2110 int id, rem, retval; 2111 unsigned long rc; 2112 2113 hdd_enter_dev(dev); 2114 2115 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 2116 hdd_err("Command not allowed in FTM mode"); 2117 return -EPERM; 2118 } 2119 2120 retval = wlan_hdd_validate_context(hdd_ctx); 2121 if (0 != retval) 2122 return -EINVAL; 2123 2124 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, 2125 data, data_len, 2126 wlan_hdd_extscan_config_policy)) { 2127 hdd_err("Invalid ATTR"); 2128 return -EINVAL; 2129 } 2130 2131 params = qdf_mem_malloc(sizeof(*params)); 2132 if (!params) 2133 return -ENOMEM; 2134 2135 /* assume the worst until proven otherwise */ 2136 retval = -EINVAL; 2137 2138 /* Parse and fetch request Id */ 2139 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 2140 if (!tb[id]) { 2141 hdd_err("attr request id failed"); 2142 goto fail; 2143 } 2144 2145 params->request_id = nla_get_u32(tb[id]); 2146 hdd_debug("Req Id %d", params->request_id); 2147 2148 /* Parse and fetch RSSI sample size */ 2149 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE; 2150 if (!tb[id]) { 2151 hdd_err("attr RSSI sample size failed"); 2152 goto fail; 2153 } 2154 params->rssi_sample_size = nla_get_u32(tb[id]); 2155 hdd_debug("RSSI sample size %u", params->rssi_sample_size); 2156 2157 /* Parse and fetch lost AP sample size */ 2158 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE; 2159 if (!tb[id]) { 2160 hdd_err("attr lost AP sample size failed"); 2161 goto fail; 2162 } 2163 params->lostap_sample_size = nla_get_u32(tb[id]); 2164 hdd_debug("Lost AP sample size %u", params->lostap_sample_size); 2165 2166 /* Parse and fetch AP min breaching */ 2167 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING; 2168 if (!tb[id]) { 2169 hdd_err("attr AP min breaching"); 2170 goto fail; 2171 } 2172 params->min_breaching = nla_get_u32(tb[id]); 2173 hdd_debug("AP min breaching %u", params->min_breaching); 2174 2175 /* Parse and fetch number of APs */ 2176 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP; 2177 if (!tb[id]) { 2178 hdd_err("attr number of AP failed"); 2179 goto fail; 2180 } 2181 params->num_ap = nla_get_u32(tb[id]); 2182 if (params->num_ap > WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS) { 2183 hdd_err("Number of AP %u exceeds max %u", 2184 params->num_ap, 2185 WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS); 2186 goto fail; 2187 } 2188 2189 params->vdev_id = adapter->vdev_id; 2190 hdd_debug("Number of AP %d Vdev Id %d", 2191 params->num_ap, params->vdev_id); 2192 2193 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM; 2194 if (!tb[id]) { 2195 hdd_err("attr ap threshold failed"); 2196 goto fail; 2197 } 2198 i = 0; 2199 nla_for_each_nested(apth, tb[id], rem) { 2200 2201 if (i == params->num_ap) { 2202 hdd_warn("Ignoring excess AP"); 2203 break; 2204 } 2205 2206 retval = hdd_parse_ap_rssi_threshold(apth, ¶ms->ap[i]); 2207 if (retval) 2208 goto fail; 2209 2210 i++; 2211 } 2212 if (i < params->num_ap) { 2213 hdd_warn("Number of AP %u less than expected %u", 2214 i, params->num_ap); 2215 params->num_ap = i; 2216 } 2217 2218 context = &ext_scan_context; 2219 spin_lock(&context->context_lock); 2220 INIT_COMPLETION(context->response_event); 2221 context->request_id = params->request_id; 2222 spin_unlock(&context->context_lock); 2223 2224 status = sme_set_significant_change(hdd_ctx->mac_handle, params); 2225 if (!QDF_IS_STATUS_SUCCESS(status)) { 2226 hdd_err("sme_set_significant_change failed(err=%d)", status); 2227 retval = qdf_status_to_os_return(status); 2228 goto fail; 2229 } 2230 2231 /* request was sent -- wait for the response */ 2232 rc = wait_for_completion_timeout(&context->response_event, 2233 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 2234 2235 if (!rc) { 2236 hdd_err("sme_set_significant_change timed out"); 2237 retval = -ETIMEDOUT; 2238 } else { 2239 spin_lock(&context->context_lock); 2240 if (context->request_id == params->request_id) 2241 retval = context->response_status; 2242 else 2243 retval = -EINVAL; 2244 spin_unlock(&context->context_lock); 2245 } 2246 hdd_exit(); 2247 2248 fail: 2249 qdf_mem_free(params); 2250 return retval; 2251 } 2252 2253 /** 2254 * wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change 2255 * @wiphy: Pointer to wireless phy 2256 * @wdev: Pointer to wireless device 2257 * @data: Pointer to data 2258 * @data_len: Data length 2259 * 2260 * Return: 0 on success, negative errno on failure 2261 */ 2262 int wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy, 2263 struct wireless_dev *wdev, 2264 const void *data, int data_len) 2265 { 2266 int errno; 2267 struct osif_vdev_sync *vdev_sync; 2268 2269 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 2270 if (errno) 2271 return errno; 2272 2273 errno = __wlan_hdd_cfg80211_extscan_set_significant_change(wiphy, wdev, 2274 data, 2275 data_len); 2276 2277 osif_vdev_sync_op_stop(vdev_sync); 2278 2279 return errno; 2280 } 2281 2282 /** 2283 * hdd_extscan_update_dwell_time_limits() - update dwell times 2284 * @req_msg: Pointer to request message 2285 * @bkt_idx: Index of current bucket being processed 2286 * @active_min: minimum active dwell time 2287 * @active_max: maximum active dwell time 2288 * @passive_min: minimum passive dwell time 2289 * @passive_max: maximum passive dwell time 2290 * 2291 * Return: none 2292 */ 2293 static void 2294 hdd_extscan_update_dwell_time_limits(struct wifi_scan_cmd_req_params *req_msg, 2295 uint32_t bkt_idx, uint32_t active_min, 2296 uint32_t active_max, uint32_t passive_min, 2297 uint32_t passive_max) 2298 { 2299 /* update per-bucket dwell times */ 2300 if (req_msg->buckets[bkt_idx].min_dwell_time_active > 2301 active_min) { 2302 req_msg->buckets[bkt_idx].min_dwell_time_active = 2303 active_min; 2304 } 2305 if (req_msg->buckets[bkt_idx].max_dwell_time_active < 2306 active_max) { 2307 req_msg->buckets[bkt_idx].max_dwell_time_active = 2308 active_max; 2309 } 2310 if (req_msg->buckets[bkt_idx].min_dwell_time_passive > 2311 passive_min) { 2312 req_msg->buckets[bkt_idx].min_dwell_time_passive = 2313 passive_min; 2314 } 2315 if (req_msg->buckets[bkt_idx].max_dwell_time_passive < 2316 passive_max) { 2317 req_msg->buckets[bkt_idx].max_dwell_time_passive = 2318 passive_max; 2319 } 2320 /* update dwell-time across all buckets */ 2321 if (req_msg->min_dwell_time_active > 2322 req_msg->buckets[bkt_idx].min_dwell_time_active) { 2323 req_msg->min_dwell_time_active = 2324 req_msg->buckets[bkt_idx].min_dwell_time_active; 2325 } 2326 if (req_msg->max_dwell_time_active < 2327 req_msg->buckets[bkt_idx].max_dwell_time_active) { 2328 req_msg->max_dwell_time_active = 2329 req_msg->buckets[bkt_idx].max_dwell_time_active; 2330 } 2331 if (req_msg->min_dwell_time_passive > 2332 req_msg->buckets[bkt_idx].min_dwell_time_passive) { 2333 req_msg->min_dwell_time_passive = 2334 req_msg->buckets[bkt_idx].min_dwell_time_passive; 2335 } 2336 if (req_msg->max_dwell_time_passive > 2337 req_msg->buckets[bkt_idx].max_dwell_time_passive) { 2338 req_msg->max_dwell_time_passive = 2339 req_msg->buckets[bkt_idx].max_dwell_time_passive; 2340 } 2341 } 2342 2343 /** 2344 * hdd_extscan_channel_max_reached() - channel max reached 2345 * @req: extscan request structure 2346 * @total_channels: total number of channels 2347 * 2348 * Return: true if total channels reached max, false otherwise 2349 */ 2350 static bool 2351 hdd_extscan_channel_max_reached(struct wifi_scan_cmd_req_params *req, 2352 uint8_t total_channels) 2353 { 2354 if (total_channels == WMI_WLAN_EXTSCAN_MAX_CHANNELS) { 2355 hdd_warn("max #of channels %d reached, take only first %d bucket(s)", 2356 total_channels, req->num_buckets); 2357 return true; 2358 } 2359 return false; 2360 } 2361 2362 /** 2363 * hdd_extscan_start_fill_bucket_channel_spec() - fill bucket channel spec 2364 * @hdd_ctx: HDD global context 2365 * @req_msg: Pointer to request structure 2366 * @bucket_attr: pointer to bucket attribute 2367 * 2368 * Return: 0 on success; error number otherwise 2369 */ 2370 static int hdd_extscan_start_fill_bucket_channel_spec( 2371 struct hdd_context *hdd_ctx, 2372 struct wifi_scan_cmd_req_params *req_msg, 2373 struct nlattr *bucket_attr) 2374 { 2375 mac_handle_t mac_handle; 2376 struct nlattr *bucket_tb[EXTSCAN_PARAM_MAX + 1]; 2377 struct nlattr *channel_tb[EXTSCAN_PARAM_MAX + 1]; 2378 struct nlattr *buckets; 2379 struct nlattr *channels; 2380 int id, rem1, rem2; 2381 QDF_STATUS status; 2382 uint8_t bkt_index, j, num_channels, total_channels = 0; 2383 uint32_t expected_buckets; 2384 uint32_t expected_channels; 2385 uint32_t chan_list[CFG_VALID_CHANNEL_LIST_LEN] = {0}; 2386 uint32_t extscan_active_min_chn_time; 2387 uint32_t min_dwell_time_active_bucket; 2388 uint32_t max_dwell_time_active_bucket; 2389 uint32_t min_dwell_time_passive_bucket; 2390 uint32_t max_dwell_time_passive_bucket; 2391 struct wifi_scan_bucket_params *bucket; 2392 struct wifi_scan_channelspec_params *channel; 2393 struct nlattr *channel_attr; 2394 2395 ucfg_extscan_get_active_min_time(hdd_ctx->psoc, 2396 &extscan_active_min_chn_time); 2397 ucfg_extscan_get_active_max_time(hdd_ctx->psoc, 2398 &max_dwell_time_active_bucket); 2399 ucfg_extscan_get_passive_max_time(hdd_ctx->psoc, 2400 &max_dwell_time_passive_bucket); 2401 2402 min_dwell_time_active_bucket = max_dwell_time_active_bucket; 2403 min_dwell_time_passive_bucket = max_dwell_time_passive_bucket; 2404 2405 req_msg->min_dwell_time_active = 2406 req_msg->max_dwell_time_active = max_dwell_time_active_bucket; 2407 2408 req_msg->min_dwell_time_passive = 2409 req_msg->max_dwell_time_passive = max_dwell_time_passive_bucket; 2410 2411 expected_buckets = req_msg->num_buckets; 2412 req_msg->num_buckets = 0; 2413 bkt_index = 0; 2414 2415 mac_handle = hdd_ctx->mac_handle; 2416 nla_for_each_nested(buckets, bucket_attr, rem1) { 2417 2418 if (bkt_index >= expected_buckets) { 2419 hdd_warn("ignoring excess buckets"); 2420 break; 2421 } 2422 2423 if (wlan_cfg80211_nla_parse(bucket_tb, EXTSCAN_PARAM_MAX, 2424 nla_data(buckets), nla_len(buckets), 2425 wlan_hdd_extscan_config_policy)) { 2426 hdd_err("nla_parse failed"); 2427 return -EINVAL; 2428 } 2429 2430 bucket = &req_msg->buckets[bkt_index]; 2431 2432 /* Parse and fetch bucket spec */ 2433 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX; 2434 if (!bucket_tb[id]) { 2435 hdd_err("attr bucket index failed"); 2436 return -EINVAL; 2437 } 2438 bucket->bucket = nla_get_u8(bucket_tb[id]); 2439 2440 /* Parse and fetch wifi band */ 2441 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND; 2442 if (!bucket_tb[id]) { 2443 hdd_err("attr wifi band failed"); 2444 return -EINVAL; 2445 } 2446 bucket->band = nla_get_u8(bucket_tb[id]); 2447 2448 /* Parse and fetch period */ 2449 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD; 2450 if (!bucket_tb[id]) { 2451 hdd_err("attr period failed"); 2452 return -EINVAL; 2453 } 2454 bucket->period = nla_get_u32(bucket_tb[id]); 2455 2456 /* Parse and fetch report events */ 2457 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS; 2458 if (!bucket_tb[id]) { 2459 hdd_err("attr report events failed"); 2460 return -EINVAL; 2461 } 2462 bucket->report_events = nla_get_u8(bucket_tb[id]); 2463 2464 /* Parse and fetch max period */ 2465 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD; 2466 if (!bucket_tb[id]) { 2467 hdd_err("attr max period failed"); 2468 return -EINVAL; 2469 } 2470 bucket->max_period = nla_get_u32(bucket_tb[id]); 2471 2472 /* Parse and fetch base */ 2473 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE; 2474 if (!bucket_tb[id]) { 2475 hdd_err("attr base failed"); 2476 return -EINVAL; 2477 } 2478 bucket->exponent = nla_get_u32(bucket_tb[id]); 2479 2480 /* Parse and fetch step count */ 2481 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT; 2482 if (!bucket_tb[id]) { 2483 hdd_err("attr step count failed"); 2484 return -EINVAL; 2485 } 2486 bucket->step_count = nla_get_u32(bucket_tb[id]); 2487 2488 hdd_debug("Bucket spec Index: %d Wifi band: %d period: %d report events: %d max period: %u base: %u Step count: %u", 2489 bucket->bucket, 2490 bucket->band, 2491 bucket->period, 2492 bucket->report_events, 2493 bucket->max_period, 2494 bucket->exponent, 2495 bucket->step_count); 2496 2497 /* start with known good values for bucket dwell times */ 2498 bucket->min_dwell_time_active = max_dwell_time_active_bucket; 2499 bucket->max_dwell_time_active = max_dwell_time_active_bucket; 2500 bucket->min_dwell_time_passive = max_dwell_time_passive_bucket; 2501 bucket->max_dwell_time_passive = max_dwell_time_passive_bucket; 2502 2503 /* Framework shall pass the channel list if the input 2504 * WiFi band is WMI_WIFI_BAND_UNSPECIFIED. If the 2505 * input WiFi band is specified (any value other than 2506 * WMI_WIFI_BAND_UNSPECIFIED) then driver populates 2507 * the channel list. 2508 */ 2509 if (bucket->band != WMI_WIFI_BAND_UNSPECIFIED) { 2510 if (hdd_extscan_channel_max_reached(req_msg, 2511 total_channels)) 2512 return 0; 2513 2514 num_channels = 0; 2515 hdd_debug("WiFi band is specified, driver to fill channel list"); 2516 status = sme_get_valid_channels_by_band(mac_handle, 2517 bucket->band, 2518 chan_list, 2519 &num_channels); 2520 if (!QDF_IS_STATUS_SUCCESS(status)) { 2521 hdd_err("sme_get_valid_channels_by_band failed (err=%d)", 2522 status); 2523 return -EINVAL; 2524 } 2525 hdd_debug("before trimming, num_channels: %d", 2526 num_channels); 2527 2528 bucket->num_channels = 2529 QDF_MIN(num_channels, 2530 (WMI_WLAN_EXTSCAN_MAX_CHANNELS - 2531 total_channels)); 2532 hdd_debug("Adj Num channels/bucket: %d total_channels: %d", 2533 bucket->num_channels, total_channels); 2534 total_channels += bucket->num_channels; 2535 2536 for (j = 0; j < bucket->num_channels; j++) { 2537 channel = &bucket->channels[j]; 2538 2539 channel->channel = chan_list[j]; 2540 channel->channel_class = 0; 2541 if ((wlan_reg_get_channel_state_for_freq( 2542 hdd_ctx->pdev, chan_list[j])) != 2543 CHANNEL_STATE_ENABLE) { 2544 channel->passive = 1; 2545 channel->dwell_time_ms = 2546 max_dwell_time_passive_bucket; 2547 /* reconfigure per-bucket dwell time */ 2548 if (min_dwell_time_passive_bucket > 2549 channel->dwell_time_ms) { 2550 min_dwell_time_passive_bucket = 2551 channel->dwell_time_ms; 2552 } 2553 if (max_dwell_time_passive_bucket < 2554 channel->dwell_time_ms) { 2555 max_dwell_time_passive_bucket = 2556 channel->dwell_time_ms; 2557 } 2558 2559 } else { 2560 channel->passive = 0; 2561 channel->dwell_time_ms = 2562 max_dwell_time_active_bucket; 2563 /* reconfigure per-bucket dwell times */ 2564 if (min_dwell_time_active_bucket > 2565 channel->dwell_time_ms) { 2566 min_dwell_time_active_bucket = 2567 channel->dwell_time_ms; 2568 } 2569 if (max_dwell_time_active_bucket < 2570 channel->dwell_time_ms) { 2571 max_dwell_time_active_bucket = 2572 channel->dwell_time_ms; 2573 } 2574 2575 } 2576 2577 hdd_debug("Channel: %u Passive: %u Dwell time: %u ms Class: %u", 2578 channel->channel, 2579 channel->passive, 2580 channel->dwell_time_ms, 2581 channel->channel_class); 2582 } 2583 2584 hdd_extscan_update_dwell_time_limits( 2585 req_msg, bkt_index, 2586 min_dwell_time_active_bucket, 2587 max_dwell_time_active_bucket, 2588 min_dwell_time_passive_bucket, 2589 max_dwell_time_passive_bucket); 2590 2591 hdd_debug("bkt_index:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d", 2592 bkt_index, 2593 bucket->min_dwell_time_active, 2594 bucket->max_dwell_time_active, 2595 bucket->min_dwell_time_passive, 2596 bucket->max_dwell_time_passive); 2597 2598 bkt_index++; 2599 req_msg->num_buckets++; 2600 continue; 2601 } 2602 2603 /* Parse and fetch number of channels */ 2604 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS; 2605 if (!bucket_tb[id]) { 2606 hdd_err("attr num channels failed"); 2607 return -EINVAL; 2608 } 2609 bucket->num_channels = nla_get_u32(bucket_tb[id]); 2610 hdd_debug("before trimming: num channels %d", 2611 bucket->num_channels); 2612 2613 bucket->num_channels = 2614 QDF_MIN(bucket->num_channels, 2615 (WMI_WLAN_EXTSCAN_MAX_CHANNELS - 2616 total_channels)); 2617 hdd_debug("Num channels/bucket: %d total_channels: %d", 2618 bucket->num_channels, total_channels); 2619 2620 if (hdd_extscan_channel_max_reached(req_msg, total_channels)) 2621 return 0; 2622 2623 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC; 2624 channel_attr = bucket_tb[id]; 2625 if (!channel_attr) { 2626 hdd_err("attr channel spec failed"); 2627 return -EINVAL; 2628 } 2629 2630 expected_channels = bucket->num_channels; 2631 bucket->num_channels = 0; 2632 2633 nla_for_each_nested(channels, channel_attr, rem2) { 2634 if ((bucket->num_channels >= expected_channels) || 2635 hdd_extscan_channel_max_reached(req_msg, 2636 total_channels)) 2637 break; 2638 2639 if (wlan_cfg80211_nla_parse(channel_tb, 2640 EXTSCAN_PARAM_MAX, 2641 nla_data(channels), 2642 nla_len(channels), 2643 wlan_hdd_extscan_config_policy)) { 2644 hdd_err("nla_parse failed"); 2645 return -EINVAL; 2646 } 2647 2648 channel = &bucket->channels[bucket->num_channels]; 2649 /* Parse and fetch channel */ 2650 if (!channel_tb[ 2651 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]) { 2652 hdd_err("attr channel failed"); 2653 return -EINVAL; 2654 } 2655 channel->channel = 2656 nla_get_u32(channel_tb[ 2657 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]); 2658 hdd_debug("channel %u", 2659 channel->channel); 2660 2661 /* Parse and fetch dwell time */ 2662 if (!channel_tb[ 2663 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]) { 2664 hdd_err("attr dwelltime failed"); 2665 return -EINVAL; 2666 } 2667 channel->dwell_time_ms = 2668 nla_get_u32(channel_tb[ 2669 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]); 2670 2671 /* Override dwell time if required */ 2672 if (channel->dwell_time_ms < 2673 extscan_active_min_chn_time || 2674 channel->dwell_time_ms > 2675 max_dwell_time_active_bucket) { 2676 hdd_debug("WiFi band is unspecified, dwellTime:%d", 2677 channel->dwell_time_ms); 2678 2679 if ((wlan_reg_get_channel_state_for_freq( 2680 hdd_ctx->pdev, channel->channel)) != 2681 CHANNEL_STATE_ENABLE) { 2682 channel->dwell_time_ms = 2683 max_dwell_time_passive_bucket; 2684 } else { 2685 channel->dwell_time_ms = 2686 max_dwell_time_active_bucket; 2687 } 2688 } 2689 2690 hdd_debug("New Dwell time %u ms", 2691 channel->dwell_time_ms); 2692 2693 if ((wlan_reg_get_channel_state_for_freq( 2694 hdd_ctx->pdev, channel->channel)) != 2695 CHANNEL_STATE_ENABLE) { 2696 if (min_dwell_time_passive_bucket > 2697 channel->dwell_time_ms) { 2698 min_dwell_time_passive_bucket = 2699 channel->dwell_time_ms; 2700 } 2701 if (max_dwell_time_passive_bucket < 2702 channel->dwell_time_ms) { 2703 max_dwell_time_passive_bucket = 2704 channel->dwell_time_ms; 2705 } 2706 } else { 2707 if (min_dwell_time_active_bucket > 2708 channel->dwell_time_ms) { 2709 min_dwell_time_active_bucket = 2710 channel->dwell_time_ms; 2711 } 2712 if (max_dwell_time_active_bucket < 2713 channel->dwell_time_ms) { 2714 max_dwell_time_active_bucket = 2715 channel->dwell_time_ms; 2716 } 2717 } 2718 2719 /* Parse and fetch channel spec passive */ 2720 if (!channel_tb[ 2721 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]) { 2722 hdd_err("attr channel spec passive failed"); 2723 return -EINVAL; 2724 } 2725 channel->passive = 2726 nla_get_u8(channel_tb[ 2727 QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]); 2728 hdd_debug("Chnl spec passive %u", 2729 channel->passive); 2730 /* Override scan type if required */ 2731 if ((wlan_reg_get_channel_state_for_freq( 2732 hdd_ctx->pdev, 2733 channel->channel)) 2734 != CHANNEL_STATE_ENABLE) { 2735 channel->passive = true; 2736 } else { 2737 channel->passive = false; 2738 } 2739 total_channels++; 2740 bucket->num_channels++; 2741 } 2742 2743 if (bucket->num_channels != expected_channels) 2744 hdd_warn("channels: Expected %u got %u", 2745 expected_channels, 2746 bucket->num_channels); 2747 2748 hdd_extscan_update_dwell_time_limits( 2749 req_msg, bkt_index, 2750 min_dwell_time_active_bucket, 2751 max_dwell_time_active_bucket, 2752 min_dwell_time_passive_bucket, 2753 max_dwell_time_passive_bucket); 2754 2755 hdd_debug("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d", 2756 bkt_index, 2757 bucket->min_dwell_time_active, 2758 bucket->max_dwell_time_active, 2759 bucket->min_dwell_time_passive, 2760 bucket->max_dwell_time_passive); 2761 2762 bkt_index++; 2763 req_msg->num_buckets++; 2764 } 2765 2766 hdd_debug("Global: actv_min:%d actv_max:%d pass_min:%d pass_max:%d", 2767 req_msg->min_dwell_time_active, 2768 req_msg->max_dwell_time_active, 2769 req_msg->min_dwell_time_passive, 2770 req_msg->max_dwell_time_passive); 2771 return 0; 2772 } 2773 2774 /* 2775 * hdd_extscan_map_usr_drv_config_flags() - map userspace to driver config flags 2776 * @config_flags - [input] configuration flags. 2777 * 2778 * This function maps user space received configuration flags to 2779 * driver representation. 2780 * 2781 * Return: configuration flags 2782 */ 2783 static uint32_t hdd_extscan_map_usr_drv_config_flags(uint32_t config_flags) 2784 { 2785 uint32_t configuration_flags = 0; 2786 2787 if (config_flags & EXTSCAN_LP_EXTENDED_BATCHING) 2788 configuration_flags |= EXTSCAN_LP_EXTENDED_BATCHING; 2789 2790 return configuration_flags; 2791 } 2792 2793 /** 2794 * __wlan_hdd_cfg80211_extscan_start() - ext scan start 2795 * @wiphy: Pointer to wireless phy 2796 * @wdev: Pointer to wireless device 2797 * @data: Pointer to data 2798 * @data_len: Length of @data 2799 * 2800 * Return: 0 on success; error number otherwise 2801 */ 2802 static int 2803 __wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, 2804 struct wireless_dev *wdev, 2805 const void *data, 2806 int data_len) 2807 { 2808 struct wifi_scan_cmd_req_params *params; 2809 struct net_device *dev = wdev->netdev; 2810 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 2811 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 2812 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 2813 struct hdd_ext_scan_context *context; 2814 uint32_t num_buckets; 2815 QDF_STATUS status; 2816 int retval, id; 2817 unsigned long rc; 2818 2819 hdd_enter_dev(dev); 2820 2821 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 2822 hdd_err("Command not allowed in FTM mode"); 2823 return -EPERM; 2824 } 2825 2826 if (QDF_NDI_MODE == adapter->device_mode) { 2827 hdd_err("Command not allowed for NDI interface"); 2828 return -EPERM; 2829 } 2830 2831 retval = wlan_hdd_validate_context(hdd_ctx); 2832 if (0 != retval) 2833 return -EINVAL; 2834 2835 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 2836 hdd_err("extscan not supported"); 2837 return -ENOTSUPP; 2838 } 2839 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, 2840 wlan_hdd_extscan_config_policy)) { 2841 hdd_err("Invalid ATTR"); 2842 return -EINVAL; 2843 } 2844 2845 params = qdf_mem_malloc(sizeof(*params)); 2846 if (!params) 2847 return -ENOMEM; 2848 2849 /* assume the worst until proven otherwise */ 2850 retval = -EINVAL; 2851 2852 /* Parse and fetch request Id */ 2853 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 2854 if (!tb[id]) { 2855 hdd_err("attr request id failed"); 2856 goto fail; 2857 } 2858 2859 params->request_id = nla_get_u32(tb[id]); 2860 params->vdev_id = adapter->vdev_id; 2861 2862 /* Parse and fetch base period */ 2863 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD; 2864 if (!tb[id]) { 2865 hdd_err("attr base period failed"); 2866 goto fail; 2867 } 2868 params->base_period = nla_get_u32(tb[id]); 2869 2870 /* Parse and fetch max AP per scan */ 2871 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN; 2872 if (!tb[id]) { 2873 hdd_err("attr max_ap_per_scan failed"); 2874 goto fail; 2875 } 2876 params->max_ap_per_scan = nla_get_u32(tb[id]); 2877 2878 /* Parse and fetch report threshold percent */ 2879 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT; 2880 if (!tb[id]) { 2881 hdd_err("attr report_threshold percent failed"); 2882 goto fail; 2883 } 2884 params->report_threshold_percent = nla_get_u8(tb[id]); 2885 2886 /* Parse and fetch report threshold num scans */ 2887 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS; 2888 if (!tb[id]) { 2889 hdd_err("attr report_threshold num scans failed"); 2890 goto fail; 2891 } 2892 params->report_threshold_num_scans = nla_get_u8(tb[id]); 2893 hdd_debug("Req Id: %d Vdev Id: %d Base Period: %d Max AP per Scan: %d Report Threshold percent: %d Report Threshold num scans: %d", 2894 params->request_id, params->vdev_id, 2895 params->base_period, params->max_ap_per_scan, 2896 params->report_threshold_percent, 2897 params->report_threshold_num_scans); 2898 2899 /* Parse and fetch number of buckets */ 2900 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS; 2901 if (!tb[id]) { 2902 hdd_err("attr number of buckets failed"); 2903 goto fail; 2904 } 2905 num_buckets = nla_get_u8(tb[id]); 2906 2907 if (num_buckets > WMI_WLAN_EXTSCAN_MAX_BUCKETS) { 2908 hdd_warn("Exceeded MAX number of buckets: %d", 2909 WMI_WLAN_EXTSCAN_MAX_BUCKETS); 2910 num_buckets = WMI_WLAN_EXTSCAN_MAX_BUCKETS; 2911 } 2912 hdd_debug("Input: Number of Buckets %d", num_buckets); 2913 params->num_buckets = num_buckets; 2914 2915 /* This is optional attribute, if not present set it to 0 */ 2916 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS; 2917 if (!tb[id]) 2918 params->configuration_flags = 0; 2919 else 2920 params->configuration_flags = 2921 hdd_extscan_map_usr_drv_config_flags( 2922 nla_get_u32(tb[id])); 2923 2924 params->extscan_adaptive_dwell_mode = 2925 ucfg_scan_get_extscan_adaptive_dwell_mode(hdd_ctx->psoc); 2926 2927 hdd_debug("Configuration flags: %u", 2928 params->configuration_flags); 2929 2930 /* Parse and fetch number the array of buckets */ 2931 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC; 2932 if (!tb[id]) { 2933 hdd_err("attr bucket spec failed"); 2934 goto fail; 2935 } 2936 retval = hdd_extscan_start_fill_bucket_channel_spec(hdd_ctx, params, 2937 tb[id]); 2938 if (retval) 2939 goto fail; 2940 2941 context = &ext_scan_context; 2942 spin_lock(&context->context_lock); 2943 INIT_COMPLETION(context->response_event); 2944 context->request_id = params->request_id; 2945 context->buckets_scanned = 0; 2946 spin_unlock(&context->context_lock); 2947 2948 status = sme_ext_scan_start(hdd_ctx->mac_handle, params); 2949 if (!QDF_IS_STATUS_SUCCESS(status)) { 2950 hdd_err("sme_ext_scan_start failed(err=%d)", status); 2951 retval = qdf_status_to_os_return(status); 2952 goto fail; 2953 } 2954 2955 hdd_ctx->ext_scan_start_since_boot = qdf_get_monotonic_boottime(); 2956 hdd_debug("Timestamp since boot: %llu", 2957 hdd_ctx->ext_scan_start_since_boot); 2958 2959 /* request was sent -- wait for the response */ 2960 rc = wait_for_completion_timeout(&context->response_event, 2961 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 2962 2963 if (!rc) { 2964 hdd_err("sme_ext_scan_start timed out"); 2965 retval = -ETIMEDOUT; 2966 } else { 2967 spin_lock(&context->context_lock); 2968 if (context->request_id == params->request_id) 2969 retval = context->response_status; 2970 else 2971 retval = -EINVAL; 2972 spin_unlock(&context->context_lock); 2973 } 2974 hdd_exit(); 2975 2976 fail: 2977 qdf_mem_free(params); 2978 return retval; 2979 } 2980 2981 /** 2982 * wlan_hdd_cfg80211_extscan_start() - start extscan 2983 * @wiphy: Pointer to wireless phy. 2984 * @wdev: Pointer to wireless device. 2985 * @data: Pointer to input data. 2986 * @data_len: Length of @data. 2987 * 2988 * Return: 0 on success, negative errno on failure 2989 */ 2990 int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, 2991 struct wireless_dev *wdev, 2992 const void *data, int data_len) 2993 { 2994 int errno; 2995 struct osif_vdev_sync *vdev_sync; 2996 2997 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 2998 if (errno) 2999 return errno; 3000 3001 errno = __wlan_hdd_cfg80211_extscan_start(wiphy, wdev, data, data_len); 3002 3003 osif_vdev_sync_op_stop(vdev_sync); 3004 3005 return errno; 3006 } 3007 3008 3009 /** 3010 * __wlan_hdd_cfg80211_extscan_stop() - ext scan stop 3011 * @wiphy: Pointer to wireless phy 3012 * @wdev: Pointer to wireless device 3013 * @data: Pointer to data 3014 * @data_len: Data length 3015 * 3016 * Return: none 3017 */ 3018 static int 3019 __wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, 3020 struct wireless_dev *wdev, 3021 const void *data, int data_len) 3022 { 3023 struct extscan_stop_req_params params; 3024 struct net_device *dev = wdev->netdev; 3025 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 3026 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 3027 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 3028 struct hdd_ext_scan_context *context; 3029 QDF_STATUS status; 3030 int id, retval; 3031 unsigned long rc; 3032 3033 hdd_enter_dev(dev); 3034 3035 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 3036 hdd_err("Command not allowed in FTM mode"); 3037 return -EPERM; 3038 } 3039 3040 retval = wlan_hdd_validate_context(hdd_ctx); 3041 if (0 != retval) 3042 return -EINVAL; 3043 3044 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 3045 hdd_err("extscan not supported"); 3046 return -ENOTSUPP; 3047 } 3048 3049 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, 3050 wlan_hdd_extscan_config_policy)) { 3051 hdd_err("Invalid ATTR"); 3052 return -EINVAL; 3053 } 3054 3055 /* Parse and fetch request Id */ 3056 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 3057 if (!tb[id]) { 3058 hdd_err("attr request id failed"); 3059 return -EINVAL; 3060 } 3061 params.request_id = nla_get_u32(tb[id]); 3062 params.vdev_id = adapter->vdev_id; 3063 hdd_debug("Req Id %d Vdev Id %d", 3064 params.request_id, params.vdev_id); 3065 3066 context = &ext_scan_context; 3067 spin_lock(&context->context_lock); 3068 INIT_COMPLETION(context->response_event); 3069 context->request_id = params.request_id; 3070 spin_unlock(&context->context_lock); 3071 3072 status = sme_ext_scan_stop(hdd_ctx->mac_handle, ¶ms); 3073 if (!QDF_IS_STATUS_SUCCESS(status)) { 3074 hdd_err("sme_ext_scan_stop failed(err=%d)", status); 3075 return qdf_status_to_os_return(status); 3076 } 3077 3078 /* request was sent -- wait for the response */ 3079 rc = wait_for_completion_timeout(&context->response_event, 3080 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 3081 3082 if (!rc) { 3083 hdd_err("sme_ext_scan_stop timed out"); 3084 retval = -ETIMEDOUT; 3085 } else { 3086 spin_lock(&context->context_lock); 3087 if (context->request_id == params.request_id) 3088 retval = context->response_status; 3089 else 3090 retval = -EINVAL; 3091 spin_unlock(&context->context_lock); 3092 } 3093 hdd_exit(); 3094 return retval; 3095 } 3096 3097 /** 3098 * wlan_hdd_cfg80211_extscan_stop() - stop extscan 3099 * @wiphy: Pointer to wireless phy. 3100 * @wdev: Pointer to wireless device. 3101 * @data: Pointer to input data. 3102 * @data_len: Length of @data. 3103 * 3104 * Return: 0 on success, negative errno on failure 3105 */ 3106 int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, 3107 struct wireless_dev *wdev, 3108 const void *data, int data_len) 3109 { 3110 int errno; 3111 struct osif_vdev_sync *vdev_sync; 3112 3113 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 3114 if (errno) 3115 return errno; 3116 3117 errno = __wlan_hdd_cfg80211_extscan_stop(wiphy, wdev, data, data_len); 3118 3119 osif_vdev_sync_op_stop(vdev_sync); 3120 3121 return errno; 3122 } 3123 3124 /** 3125 * __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hotlist 3126 * @wiphy: Pointer to wireless phy 3127 * @wdev: Pointer to wireless device 3128 * @data: Pointer to data 3129 * @data_len: Data length 3130 * 3131 * Return: none 3132 */ 3133 static int 3134 __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, 3135 struct wireless_dev *wdev, 3136 const void *data, 3137 int data_len) 3138 { 3139 struct extscan_bssid_hotlist_reset_params params; 3140 struct net_device *dev = wdev->netdev; 3141 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 3142 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 3143 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 3144 struct hdd_ext_scan_context *context; 3145 QDF_STATUS status; 3146 int id, retval; 3147 unsigned long rc; 3148 3149 hdd_enter_dev(dev); 3150 3151 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 3152 hdd_err("Command not allowed in FTM mode"); 3153 return -EPERM; 3154 } 3155 3156 retval = wlan_hdd_validate_context(hdd_ctx); 3157 if (0 != retval) 3158 return -EINVAL; 3159 3160 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 3161 hdd_err("extscan not supported"); 3162 return -ENOTSUPP; 3163 } 3164 3165 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, 3166 data, data_len, 3167 wlan_hdd_extscan_config_policy)) { 3168 hdd_err("Invalid ATTR"); 3169 return -EINVAL; 3170 } 3171 3172 /* Parse and fetch request Id */ 3173 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 3174 if (!tb[id]) { 3175 hdd_err("attr request id failed"); 3176 return -EINVAL; 3177 } 3178 3179 params.request_id = nla_get_u32(tb[id]); 3180 params.vdev_id = adapter->vdev_id; 3181 hdd_debug("Req Id %d vdev Id %d", params.request_id, params.vdev_id); 3182 3183 context = &ext_scan_context; 3184 spin_lock(&context->context_lock); 3185 INIT_COMPLETION(context->response_event); 3186 context->request_id = params.request_id; 3187 spin_unlock(&context->context_lock); 3188 3189 status = sme_reset_bss_hotlist(hdd_ctx->mac_handle, ¶ms); 3190 if (!QDF_IS_STATUS_SUCCESS(status)) { 3191 hdd_err("sme_reset_bss_hotlist failed(err=%d)", status); 3192 return qdf_status_to_os_return(status); 3193 } 3194 3195 /* request was sent -- wait for the response */ 3196 rc = wait_for_completion_timeout 3197 (&context->response_event, 3198 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 3199 if (!rc) { 3200 hdd_err("sme_reset_bss_hotlist timed out"); 3201 retval = -ETIMEDOUT; 3202 } else { 3203 spin_lock(&context->context_lock); 3204 if (context->request_id == params.request_id) 3205 retval = context->response_status; 3206 else 3207 retval = -EINVAL; 3208 spin_unlock(&context->context_lock); 3209 } 3210 hdd_exit(); 3211 return retval; 3212 } 3213 3214 /** 3215 * wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hot list 3216 * @wiphy: Pointer to wireless phy 3217 * @wdev: Pointer to wireless device 3218 * @data: Pointer to data 3219 * @data_len: Data length 3220 * 3221 * Return: 0 on success, negative errno on failure 3222 */ 3223 int wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, 3224 struct wireless_dev *wdev, 3225 const void *data, 3226 int data_len) 3227 { 3228 int errno; 3229 struct osif_vdev_sync *vdev_sync; 3230 3231 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 3232 if (errno) 3233 return errno; 3234 3235 errno = __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(wiphy, wdev, 3236 data, data_len); 3237 3238 osif_vdev_sync_op_stop(vdev_sync); 3239 3240 return errno; 3241 } 3242 3243 /** 3244 * __wlan_hdd_cfg80211_extscan_reset_significant_change() - 3245 * reset significant change 3246 * @wiphy: Pointer to wireless phy 3247 * @wdev: Pointer to wireless device 3248 * @data: Pointer to data 3249 * @data_len: Data length 3250 * 3251 * Return: none 3252 */ 3253 static int 3254 __wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy, 3255 struct wireless_dev *wdev, 3256 const void *data, 3257 int data_len) 3258 { 3259 struct extscan_capabilities_reset_params params; 3260 struct net_device *dev = wdev->netdev; 3261 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 3262 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 3263 struct nlattr *tb[EXTSCAN_PARAM_MAX + 1]; 3264 struct hdd_ext_scan_context *context; 3265 QDF_STATUS status; 3266 int id, retval; 3267 unsigned long rc; 3268 3269 hdd_enter_dev(dev); 3270 3271 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 3272 hdd_err("Command not allowed in FTM mode"); 3273 return -EPERM; 3274 } 3275 3276 retval = wlan_hdd_validate_context(hdd_ctx); 3277 if (0 != retval) 3278 return -EINVAL; 3279 3280 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 3281 hdd_err("extscan not supported"); 3282 return -ENOTSUPP; 3283 } 3284 3285 if (wlan_cfg80211_nla_parse(tb, EXTSCAN_PARAM_MAX, data, data_len, 3286 wlan_hdd_extscan_config_policy)) { 3287 hdd_err("Invalid ATTR"); 3288 return -EINVAL; 3289 } 3290 3291 /* Parse and fetch request Id */ 3292 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 3293 if (!tb[id]) { 3294 hdd_err("attr request id failed"); 3295 return -EINVAL; 3296 } 3297 3298 params.request_id = nla_get_u32(tb[id]); 3299 params.vdev_id = adapter->vdev_id; 3300 hdd_debug("Req Id %d Vdev Id %d", params.request_id, params.vdev_id); 3301 3302 context = &ext_scan_context; 3303 spin_lock(&context->context_lock); 3304 INIT_COMPLETION(context->response_event); 3305 context->request_id = params.request_id; 3306 spin_unlock(&context->context_lock); 3307 3308 status = sme_reset_significant_change(hdd_ctx->mac_handle, ¶ms); 3309 if (!QDF_IS_STATUS_SUCCESS(status)) { 3310 hdd_err("sme_reset_significant_change failed(err=%d)", 3311 status); 3312 return -EINVAL; 3313 } 3314 3315 /* request was sent -- wait for the response */ 3316 rc = wait_for_completion_timeout(&context->response_event, 3317 msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); 3318 3319 if (!rc) { 3320 hdd_err("sme_ResetSignificantChange timed out"); 3321 retval = -ETIMEDOUT; 3322 } else { 3323 spin_lock(&context->context_lock); 3324 if (context->request_id == params.request_id) 3325 retval = context->response_status; 3326 else 3327 retval = -EINVAL; 3328 spin_unlock(&context->context_lock); 3329 } 3330 hdd_exit(); 3331 return retval; 3332 } 3333 3334 /** 3335 * wlan_hdd_cfg80211_extscan_reset_significant_change() - reset significant 3336 * change 3337 * @wiphy: Pointer to wireless phy 3338 * @wdev: Pointer to wireless device 3339 * @data: Pointer to data 3340 * @data_len: Data length 3341 * 3342 * Return: 0 on success, negative errno on failure 3343 */ 3344 int wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy, 3345 struct wireless_dev *wdev, 3346 const void *data, int data_len) 3347 { 3348 int errno; 3349 struct osif_vdev_sync *vdev_sync; 3350 3351 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 3352 if (errno) 3353 return errno; 3354 3355 errno = __wlan_hdd_cfg80211_extscan_reset_significant_change(wiphy, 3356 wdev, 3357 data, 3358 data_len); 3359 3360 osif_vdev_sync_op_stop(vdev_sync); 3361 3362 return errno; 3363 } 3364 3365 3366 /** 3367 * hdd_extscan_epno_fill_network() - epno fill single network 3368 * @network: aggregate network attribute 3369 * @nw: epno network record to be filled 3370 * 3371 * This function takes a single network block NL vendor attribute from 3372 * @network and decodes it into the internal record @nw. 3373 * 3374 * Return: 0 on success, error number otherwise 3375 */ 3376 static int 3377 hdd_extscan_epno_fill_network(struct nlattr *network, 3378 struct wifi_epno_network_params *nw) 3379 { 3380 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; 3381 int id, ssid_len; 3382 uint8_t *ssid; 3383 3384 if (!network) { 3385 hdd_err("attr network attr failed"); 3386 return -EINVAL; 3387 } 3388 3389 if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, 3390 nla_data(network), 3391 nla_len(network), 3392 wlan_hdd_pno_config_policy)) { 3393 hdd_err("nla_parse failed"); 3394 return -EINVAL; 3395 } 3396 3397 /* Parse and fetch ssid */ 3398 id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID; 3399 if (!tb[id]) { 3400 hdd_err("attr network ssid failed"); 3401 return -EINVAL; 3402 } 3403 ssid_len = nla_len(tb[id]); 3404 3405 /* nla_parse will detect overflow but not underflow */ 3406 if (0 == ssid_len) { 3407 hdd_err("zero ssid length"); 3408 return -EINVAL; 3409 } 3410 3411 /* Decrement by 1, don't count null character */ 3412 ssid_len--; 3413 3414 nw->ssid.length = ssid_len; 3415 hdd_debug("network ssid length %d", ssid_len); 3416 ssid = nla_data(tb[id]); 3417 qdf_mem_copy(nw->ssid.ssid, ssid, ssid_len); 3418 hdd_debug("Ssid (%.*s)", nw->ssid.length, nw->ssid.ssid); 3419 3420 /* Parse and fetch epno flags */ 3421 id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS; 3422 if (!tb[id]) { 3423 hdd_err("attr epno flags failed"); 3424 return -EINVAL; 3425 } 3426 nw->flags = nla_get_u8(tb[id]); 3427 hdd_debug("flags %u", nw->flags); 3428 3429 /* Parse and fetch auth bit */ 3430 id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT; 3431 if (!tb[id]) { 3432 hdd_err("attr auth bit failed"); 3433 return -EINVAL; 3434 } 3435 nw->auth_bit_field = nla_get_u8(tb[id]); 3436 hdd_debug("auth bit %u", nw->auth_bit_field); 3437 3438 return 0; 3439 } 3440 3441 /** 3442 * hdd_extscan_epno_fill_network_list() - epno fill network list 3443 * @req_msg: request message 3444 * @networks: aggregate network list attribute 3445 * 3446 * This function reads the network block NL vendor attributes from 3447 * @networks and fills in the epno request message @req_msg. 3448 * 3449 * Return: 0 on success, error number otherwise 3450 */ 3451 static int 3452 hdd_extscan_epno_fill_network_list(struct wifi_enhanced_pno_params *req_msg, 3453 struct nlattr *networks) 3454 { 3455 struct nlattr *network; 3456 int rem; 3457 uint32_t index; 3458 uint32_t expected_networks; 3459 struct wifi_epno_network_params *nw; 3460 3461 if (!networks) { 3462 hdd_err("attr networks list failed"); 3463 return -EINVAL; 3464 } 3465 3466 expected_networks = req_msg->num_networks; 3467 index = 0; 3468 3469 nla_for_each_nested(network, networks, rem) { 3470 if (index == expected_networks) { 3471 hdd_warn("ignoring excess networks"); 3472 break; 3473 } 3474 3475 nw = &req_msg->networks[index++]; 3476 if (hdd_extscan_epno_fill_network(network, nw)) 3477 return -EINVAL; 3478 } 3479 req_msg->num_networks = index; 3480 return 0; 3481 } 3482 3483 /** 3484 * __wlan_hdd_cfg80211_set_epno_list() - epno set network list 3485 * @wiphy: wiphy 3486 * @wdev: pointer to wireless dev 3487 * @data: data pointer 3488 * @data_len: data length 3489 * 3490 * This function reads the NL vendor attributes from @data and 3491 * fills in the epno request message. 3492 * 3493 * Return: 0 on success, error number otherwise 3494 */ 3495 static int __wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, 3496 struct wireless_dev *wdev, 3497 const void *data, 3498 int data_len) 3499 { 3500 struct wifi_enhanced_pno_params *req_msg; 3501 struct net_device *dev = wdev->netdev; 3502 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 3503 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 3504 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; 3505 struct nlattr *networks; 3506 QDF_STATUS status; 3507 uint32_t num_networks, len; 3508 int id, ret_val; 3509 3510 hdd_enter_dev(dev); 3511 3512 ret_val = wlan_hdd_validate_context(hdd_ctx); 3513 if (ret_val) 3514 return ret_val; 3515 3516 if (!ucfg_extscan_get_enable(hdd_ctx->psoc)) { 3517 hdd_err("extscan not supported"); 3518 return -ENOTSUPP; 3519 } 3520 3521 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 3522 hdd_err("Command not allowed in FTM mode"); 3523 return -EPERM; 3524 } 3525 3526 if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, 3527 data_len, wlan_hdd_pno_config_policy)) { 3528 hdd_err("Invalid ATTR"); 3529 return -EINVAL; 3530 } 3531 3532 /* Parse and fetch number of networks */ 3533 id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS; 3534 if (!tb[id]) { 3535 hdd_err("attr num networks failed"); 3536 return -EINVAL; 3537 } 3538 3539 /* 3540 * num_networks is also used as EPNO SET/RESET request. 3541 * if num_networks is zero then it is treated as RESET. 3542 */ 3543 num_networks = nla_get_u32(tb[id]); 3544 3545 if (num_networks > MAX_EPNO_NETWORKS) { 3546 hdd_debug("num of nw: %d exceeded max: %d, resetting to: %d", 3547 num_networks, MAX_EPNO_NETWORKS, MAX_EPNO_NETWORKS); 3548 num_networks = MAX_EPNO_NETWORKS; 3549 } 3550 3551 hdd_debug("num networks %u", num_networks); 3552 len = sizeof(*req_msg) + 3553 (num_networks * sizeof(req_msg->networks[0])); 3554 3555 req_msg = qdf_mem_malloc(len); 3556 if (!req_msg) 3557 return -ENOMEM; 3558 3559 req_msg->num_networks = num_networks; 3560 3561 /* Parse and fetch request Id */ 3562 id = QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID; 3563 if (!tb[id]) { 3564 hdd_err("attr request id failed"); 3565 goto fail; 3566 } 3567 req_msg->request_id = nla_get_u32(tb[id]); 3568 hdd_debug("Req Id %u", req_msg->request_id); 3569 3570 req_msg->vdev_id = adapter->vdev_id; 3571 hdd_debug("Vdev Id %d", req_msg->vdev_id); 3572 3573 if (num_networks) { 3574 /* Parse and fetch min_5ghz_rssi */ 3575 id = QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI; 3576 if (!tb[id]) { 3577 hdd_err("min_5ghz_rssi id failed"); 3578 goto fail; 3579 } 3580 req_msg->min_5ghz_rssi = nla_get_u32(tb[id]); 3581 3582 /* Parse and fetch min_24ghz_rssi */ 3583 id = QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI; 3584 if (!tb[id]) { 3585 hdd_err("min_24ghz_rssi id failed"); 3586 goto fail; 3587 } 3588 req_msg->min_24ghz_rssi = nla_get_u32(tb[id]); 3589 3590 /* Parse and fetch initial_score_max */ 3591 id = QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX; 3592 if (!tb[id]) { 3593 hdd_err("initial_score_max id failed"); 3594 goto fail; 3595 } 3596 req_msg->initial_score_max = nla_get_u32(tb[id]); 3597 3598 /* Parse and fetch current_connection_bonus */ 3599 id = QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS; 3600 if (!tb[id]) { 3601 hdd_err("current_connection_bonus id failed"); 3602 goto fail; 3603 } 3604 req_msg->current_connection_bonus = nla_get_u32(tb[id]); 3605 3606 /* Parse and fetch same_network_bonus */ 3607 id = QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS; 3608 if (!tb[id]) { 3609 hdd_err("same_network_bonus id failed"); 3610 goto fail; 3611 } 3612 req_msg->same_network_bonus = nla_get_u32(tb[id]); 3613 3614 /* Parse and fetch secure_bonus */ 3615 id = QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS; 3616 if (!tb[id]) { 3617 hdd_err("secure_bonus id failed"); 3618 goto fail; 3619 } 3620 req_msg->secure_bonus = nla_get_u32(tb[id]); 3621 3622 /* Parse and fetch band_5ghz_bonus */ 3623 id = QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS; 3624 if (!tb[id]) { 3625 hdd_err("band_5ghz_bonus id failed"); 3626 goto fail; 3627 } 3628 req_msg->band_5ghz_bonus = nla_get_u32(tb[id]); 3629 3630 hdd_debug("min_5ghz_rssi: %d min_24ghz_rssi: %d", 3631 req_msg->min_5ghz_rssi, 3632 req_msg->min_24ghz_rssi); 3633 hdd_debug("initial_score_max: %d current_connection_bonus:%d", 3634 req_msg->initial_score_max, 3635 req_msg->current_connection_bonus); 3636 hdd_debug("Bonuses same_network: %d secure: %d band_5ghz: %d", 3637 req_msg->same_network_bonus, 3638 req_msg->secure_bonus, 3639 req_msg->band_5ghz_bonus); 3640 3641 id = QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST; 3642 networks = tb[id]; 3643 if (hdd_extscan_epno_fill_network_list(req_msg, networks)) 3644 goto fail; 3645 3646 } 3647 3648 status = sme_set_epno_list(hdd_ctx->mac_handle, req_msg); 3649 if (!QDF_IS_STATUS_SUCCESS(status)) { 3650 hdd_err("sme_set_epno_list failed(err=%d)", status); 3651 goto fail; 3652 } 3653 3654 hdd_exit(); 3655 qdf_mem_free(req_msg); 3656 return 0; 3657 3658 fail: 3659 qdf_mem_free(req_msg); 3660 return -EINVAL; 3661 } 3662 3663 /** 3664 * wlan_hdd_cfg80211_set_epno_list() - epno set network list 3665 * @wiphy: wiphy 3666 * @wdev: pointer to wireless dev 3667 * @data: data pointer 3668 * @data_len: data length 3669 * 3670 * This function reads the NL vendor attributes from %tb and 3671 * fill in the epno request message. 3672 * 3673 * Return: 0 on success, error number otherwise 3674 */ 3675 int wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, 3676 struct wireless_dev *wdev, 3677 const void *data, 3678 int data_len) 3679 { 3680 int errno; 3681 struct osif_vdev_sync *vdev_sync; 3682 3683 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 3684 if (errno) 3685 return errno; 3686 3687 errno = __wlan_hdd_cfg80211_set_epno_list(wiphy, wdev, data, data_len); 3688 3689 osif_vdev_sync_op_stop(vdev_sync); 3690 3691 return errno; 3692 } 3693 3694 /** 3695 * hdd_extscan_passpoint_fill_network() - passpoint fill single network 3696 * @network: aggregate network attribute 3697 * @nw: passpoint network record to be filled 3698 * 3699 * This function takes a single network block NL vendor attribute from 3700 * @network and decodes it into the internal record @nw. 3701 * 3702 * Return: 0 on success, error number otherwise 3703 */ 3704 static int 3705 hdd_extscan_passpoint_fill_network(struct nlattr *network, 3706 struct wifi_passpoint_network_param *nw) 3707 { 3708 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; 3709 int id; 3710 size_t len; 3711 3712 if (!network) { 3713 hdd_err("attr network attr failed"); 3714 return -EINVAL; 3715 } 3716 3717 if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, 3718 nla_data(network), 3719 nla_len(network), 3720 wlan_hdd_pno_config_policy)) { 3721 hdd_err("nla_parse failed"); 3722 return -EINVAL; 3723 } 3724 3725 /* Parse and fetch identifier */ 3726 id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID; 3727 if (!tb[id]) { 3728 hdd_err("attr passpoint id failed"); 3729 return -EINVAL; 3730 } 3731 nw->id = nla_get_u32(tb[id]); 3732 hdd_debug("Id %u", nw->id); 3733 3734 /* Parse and fetch realm */ 3735 id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM; 3736 if (!tb[id]) { 3737 hdd_err("attr realm failed"); 3738 return -EINVAL; 3739 } 3740 len = nla_strlcpy(nw->realm, tb[id], 3741 WMI_PASSPOINT_REALM_LEN); 3742 /* Don't send partial realm to firmware */ 3743 if (len >= WMI_PASSPOINT_REALM_LEN) { 3744 hdd_err("user passed invalid realm, len:%zu", len); 3745 return -EINVAL; 3746 } 3747 3748 hdd_debug("realm: %s", nw->realm); 3749 3750 /* Parse and fetch roaming consortium ids */ 3751 id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID; 3752 if (!tb[id]) { 3753 hdd_err("attr roaming consortium ids failed"); 3754 return -EINVAL; 3755 } 3756 nla_memcpy(&nw->roaming_consortium_ids, tb[id], 3757 sizeof(nw->roaming_consortium_ids)); 3758 hdd_debug("roaming consortium ids"); 3759 3760 /* Parse and fetch plmn */ 3761 id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN; 3762 if (!tb[id]) { 3763 hdd_err("attr plmn failed"); 3764 return -EINVAL; 3765 } 3766 nla_memcpy(&nw->plmn, tb[id], 3767 WMI_PASSPOINT_PLMN_LEN); 3768 hdd_debug("plmn %02x:%02x:%02x)", 3769 nw->plmn[0], 3770 nw->plmn[1], 3771 nw->plmn[2]); 3772 3773 return 0; 3774 } 3775 3776 /** 3777 * hdd_extscan_passpoint_fill_networks() - passpoint fill network list 3778 * @req_msg: request message 3779 * @networks: aggregate network list attribute 3780 * 3781 * This function reads the network block NL vendor attributes from 3782 * @networks and fills in the passpoint request message. 3783 * 3784 * Return: 0 on success, error number otherwise 3785 */ 3786 static int 3787 hdd_extscan_passpoint_fill_networks(struct wifi_passpoint_req_param *req_msg, 3788 struct nlattr *networks) 3789 { 3790 struct nlattr *network; 3791 int rem; 3792 uint32_t index; 3793 uint32_t expected_networks; 3794 struct wifi_passpoint_network_param *nw; 3795 3796 if (!networks) { 3797 hdd_err("attr networks list failed"); 3798 return -EINVAL; 3799 } 3800 3801 expected_networks = req_msg->num_networks; 3802 index = 0; 3803 3804 nla_for_each_nested(network, networks, rem) { 3805 if (index == expected_networks) { 3806 hdd_warn("ignoring excess networks"); 3807 break; 3808 } 3809 3810 nw = &req_msg->networks[index++]; 3811 if (hdd_extscan_passpoint_fill_network(network, nw)) 3812 return -EINVAL; 3813 } 3814 req_msg->num_networks = index; 3815 return 0; 3816 } 3817 3818 /** 3819 * __wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list 3820 * @wiphy: wiphy 3821 * @wdev: pointer to wireless dev 3822 * @data: data pointer 3823 * @data_len: data length 3824 * 3825 * This function reads the NL vendor attributes from %tb and 3826 * fill in the passpoint request message. 3827 * 3828 * Return: 0 on success, error number otherwise 3829 */ 3830 static int __wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, 3831 struct wireless_dev *wdev, 3832 const void *data, 3833 int data_len) 3834 { 3835 struct wifi_passpoint_req_param *req_msg; 3836 struct net_device *dev = wdev->netdev; 3837 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 3838 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 3839 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; 3840 struct nlattr *networks; 3841 uint32_t num_networks; 3842 QDF_STATUS status; 3843 int id, ret; 3844 3845 hdd_enter_dev(dev); 3846 3847 ret = wlan_hdd_validate_context(hdd_ctx); 3848 if (ret) 3849 return ret; 3850 3851 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 3852 hdd_err("Command not allowed in FTM mode"); 3853 return -EPERM; 3854 } 3855 3856 if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, 3857 data_len, wlan_hdd_pno_config_policy)) { 3858 hdd_err("Invalid ATTR"); 3859 return -EINVAL; 3860 } 3861 3862 /* Parse and fetch number of networks */ 3863 id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM; 3864 if (!tb[id]) { 3865 hdd_err("attr num networks failed"); 3866 return -EINVAL; 3867 } 3868 num_networks = nla_get_u32(tb[id]); 3869 if (num_networks > SIR_PASSPOINT_LIST_MAX_NETWORKS) { 3870 hdd_err("num networks %u exceeds max %u", 3871 num_networks, SIR_PASSPOINT_LIST_MAX_NETWORKS); 3872 return -EINVAL; 3873 } 3874 3875 hdd_debug("num networks %u", num_networks); 3876 3877 req_msg = qdf_mem_malloc(sizeof(*req_msg) + 3878 (num_networks * sizeof(req_msg->networks[0]))); 3879 if (!req_msg) 3880 return -ENOMEM; 3881 3882 req_msg->num_networks = num_networks; 3883 3884 /* Parse and fetch request Id */ 3885 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 3886 if (!tb[id]) { 3887 hdd_err("attr request id failed"); 3888 goto fail; 3889 } 3890 req_msg->request_id = nla_get_u32(tb[id]); 3891 3892 req_msg->vdev_id = adapter->vdev_id; 3893 hdd_debug("Req Id %u Vdev Id %d", 3894 req_msg->request_id, req_msg->vdev_id); 3895 3896 id = QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY; 3897 networks = tb[id]; 3898 if (hdd_extscan_passpoint_fill_networks(req_msg, networks)) 3899 goto fail; 3900 3901 status = sme_set_passpoint_list(hdd_ctx->mac_handle, req_msg); 3902 if (!QDF_IS_STATUS_SUCCESS(status)) { 3903 hdd_err("sme_set_passpoint_list failed(err=%d)", status); 3904 goto fail; 3905 } 3906 3907 hdd_exit(); 3908 qdf_mem_free(req_msg); 3909 return 0; 3910 3911 fail: 3912 qdf_mem_free(req_msg); 3913 return -EINVAL; 3914 } 3915 3916 /** 3917 * wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list 3918 * @wiphy: wiphy 3919 * @wdev: pointer to wireless dev 3920 * @data: data pointer 3921 * @data_len: data length 3922 * 3923 * This function reads the NL vendor attributes from %tb and 3924 * fill in the passpoint request message. 3925 * 3926 * Return: 0 on success, error number otherwise 3927 */ 3928 int wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, 3929 struct wireless_dev *wdev, 3930 const void *data, 3931 int data_len) 3932 { 3933 int errno; 3934 struct osif_vdev_sync *vdev_sync; 3935 3936 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 3937 if (errno) 3938 return errno; 3939 3940 errno = __wlan_hdd_cfg80211_set_passpoint_list(wiphy, wdev, 3941 data, data_len); 3942 3943 osif_vdev_sync_op_stop(vdev_sync); 3944 3945 return errno; 3946 } 3947 3948 /** 3949 * __wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list 3950 * @wiphy: wiphy 3951 * @wdev: pointer to wireless dev 3952 * @data: data pointer 3953 * @data_len: data length 3954 * 3955 * This function resets passpoint networks list 3956 * 3957 * Return: 0 on success, error number otherwise 3958 */ 3959 static int __wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, 3960 struct wireless_dev *wdev, 3961 const void *data, 3962 int data_len) 3963 { 3964 struct wifi_passpoint_req_param *req_msg; 3965 struct net_device *dev = wdev->netdev; 3966 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); 3967 struct hdd_context *hdd_ctx = wiphy_priv(wiphy); 3968 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; 3969 QDF_STATUS status; 3970 int id, ret; 3971 3972 hdd_enter_dev(dev); 3973 3974 ret = wlan_hdd_validate_context(hdd_ctx); 3975 if (ret) 3976 return ret; 3977 3978 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { 3979 hdd_err("Command not allowed in FTM mode"); 3980 return -EPERM; 3981 } 3982 3983 if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, 3984 data_len, wlan_hdd_extscan_config_policy)) { 3985 hdd_err("Invalid ATTR"); 3986 return -EINVAL; 3987 } 3988 3989 req_msg = qdf_mem_malloc(sizeof(*req_msg)); 3990 if (!req_msg) 3991 return -ENOMEM; 3992 3993 /* Parse and fetch request Id */ 3994 id = QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID; 3995 if (!tb[id]) { 3996 hdd_err("attr request id failed"); 3997 goto fail; 3998 } 3999 req_msg->request_id = nla_get_u32(tb[id]); 4000 4001 req_msg->vdev_id = adapter->vdev_id; 4002 hdd_debug("Req Id %u Vdev Id %d", 4003 req_msg->request_id, req_msg->vdev_id); 4004 4005 status = sme_reset_passpoint_list(hdd_ctx->mac_handle, req_msg); 4006 if (!QDF_IS_STATUS_SUCCESS(status)) { 4007 hdd_err("sme_reset_passpoint_list failed(err=%d)", status); 4008 goto fail; 4009 } 4010 4011 hdd_exit(); 4012 qdf_mem_free(req_msg); 4013 return 0; 4014 4015 fail: 4016 qdf_mem_free(req_msg); 4017 return -EINVAL; 4018 } 4019 4020 /** 4021 * wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list 4022 * @wiphy: wiphy 4023 * @wdev: pointer to wireless dev 4024 * @data: data pointer 4025 * @data_len: data length 4026 * 4027 * This function resets passpoint networks list 4028 * 4029 * Return: 0 on success, error number otherwise 4030 */ 4031 int wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, 4032 struct wireless_dev *wdev, 4033 const void *data, 4034 int data_len) 4035 { 4036 int errno; 4037 struct osif_vdev_sync *vdev_sync; 4038 4039 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); 4040 if (errno) 4041 return errno; 4042 4043 errno = __wlan_hdd_cfg80211_reset_passpoint_list(wiphy, wdev, 4044 data, data_len); 4045 4046 osif_vdev_sync_op_stop(vdev_sync); 4047 4048 return errno; 4049 } 4050 4051 /** 4052 * wlan_hdd_cfg80211_extscan_init() - Initialize the ExtScan feature 4053 * @hdd_ctx: Global HDD context 4054 * 4055 * Return: none 4056 */ 4057 void wlan_hdd_cfg80211_extscan_init(struct hdd_context *hdd_ctx) 4058 { 4059 init_completion(&ext_scan_context.response_event); 4060 spin_lock_init(&ext_scan_context.context_lock); 4061 } 4062 4063 #endif /* FEATURE_WLAN_EXTSCAN */ 4064