diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/scan.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/scan.c | 753 |
1 files changed, 707 insertions, 46 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index fb2a8628b8fc..e5294d01181e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c | |||
@@ -83,15 +83,29 @@ struct iwl_mvm_scan_params { | |||
83 | } dwell[IEEE80211_NUM_BANDS]; | 83 | } dwell[IEEE80211_NUM_BANDS]; |
84 | }; | 84 | }; |
85 | 85 | ||
86 | enum iwl_umac_scan_uid_type { | ||
87 | IWL_UMAC_SCAN_UID_REG_SCAN = BIT(0), | ||
88 | IWL_UMAC_SCAN_UID_SCHED_SCAN = BIT(1), | ||
89 | IWL_UMAC_SCAN_UID_ALL = IWL_UMAC_SCAN_UID_REG_SCAN | | ||
90 | IWL_UMAC_SCAN_UID_SCHED_SCAN, | ||
91 | }; | ||
92 | |||
93 | static int iwl_umac_scan_stop(struct iwl_mvm *mvm, | ||
94 | enum iwl_umac_scan_uid_type type, bool notify); | ||
95 | |||
96 | static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) | ||
97 | { | ||
98 | if (mvm->scan_rx_ant != ANT_NONE) | ||
99 | return mvm->scan_rx_ant; | ||
100 | return mvm->fw->valid_rx_ant; | ||
101 | } | ||
102 | |||
86 | static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) | 103 | static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) |
87 | { | 104 | { |
88 | u16 rx_chain; | 105 | u16 rx_chain; |
89 | u8 rx_ant; | 106 | u8 rx_ant; |
90 | 107 | ||
91 | if (mvm->scan_rx_ant != ANT_NONE) | 108 | rx_ant = iwl_mvm_scan_rx_ant(mvm); |
92 | rx_ant = mvm->scan_rx_ant; | ||
93 | else | ||
94 | rx_ant = mvm->fw->valid_rx_ant; | ||
95 | rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; | 109 | rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; |
96 | rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; | 110 | rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; |
97 | rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; | 111 | rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; |
@@ -366,6 +380,10 @@ static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm, | |||
366 | !is_sched_scan) | 380 | !is_sched_scan) |
367 | max_probe_len -= 32; | 381 | max_probe_len -= 32; |
368 | 382 | ||
383 | /* DS parameter set element is added on 2.4GHZ band if required */ | ||
384 | if (iwl_mvm_rrm_scan_needed(mvm)) | ||
385 | max_probe_len -= 3; | ||
386 | |||
369 | return max_probe_len; | 387 | return max_probe_len; |
370 | } | 388 | } |
371 | 389 | ||
@@ -537,23 +555,17 @@ int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm, | |||
537 | struct iwl_device_cmd *cmd) | 555 | struct iwl_device_cmd *cmd) |
538 | { | 556 | { |
539 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | 557 | struct iwl_rx_packet *pkt = rxb_addr(rxb); |
540 | u8 client_bitmap = 0; | ||
541 | 558 | ||
542 | if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) { | 559 | if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) && |
560 | !(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) { | ||
543 | struct iwl_sched_scan_results *notif = (void *)pkt->data; | 561 | struct iwl_sched_scan_results *notif = (void *)pkt->data; |
544 | 562 | ||
545 | client_bitmap = notif->client_bitmap; | 563 | if (!(notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN)) |
564 | return 0; | ||
546 | } | 565 | } |
547 | 566 | ||
548 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN || | 567 | IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); |
549 | client_bitmap & SCAN_CLIENT_SCHED_SCAN) { | 568 | ieee80211_sched_scan_results(mvm->hw); |
550 | if (mvm->scan_status == IWL_MVM_SCAN_SCHED) { | ||
551 | IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); | ||
552 | ieee80211_sched_scan_results(mvm->hw); | ||
553 | } else { | ||
554 | IWL_DEBUG_SCAN(mvm, "Scan results\n"); | ||
555 | } | ||
556 | } | ||
557 | 569 | ||
558 | return 0; | 570 | return 0; |
559 | } | 571 | } |
@@ -965,6 +977,20 @@ free_blacklist: | |||
965 | return ret; | 977 | return ret; |
966 | } | 978 | } |
967 | 979 | ||
980 | static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm, | ||
981 | struct cfg80211_sched_scan_request *req) | ||
982 | { | ||
983 | if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { | ||
984 | IWL_DEBUG_SCAN(mvm, | ||
985 | "Sending scheduled scan with filtering, n_match_sets %d\n", | ||
986 | req->n_match_sets); | ||
987 | return false; | ||
988 | } | ||
989 | |||
990 | IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n"); | ||
991 | return true; | ||
992 | } | ||
993 | |||
968 | int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, | 994 | int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, |
969 | struct cfg80211_sched_scan_request *req) | 995 | struct cfg80211_sched_scan_request *req) |
970 | { | 996 | { |
@@ -980,15 +1006,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, | |||
980 | .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER, | 1006 | .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER, |
981 | }; | 1007 | }; |
982 | 1008 | ||
983 | if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { | 1009 | if (iwl_mvm_scan_pass_all(mvm, req)) |
984 | IWL_DEBUG_SCAN(mvm, | ||
985 | "Sending scheduled scan with filtering, filter len %d\n", | ||
986 | req->n_match_sets); | ||
987 | } else { | ||
988 | IWL_DEBUG_SCAN(mvm, | ||
989 | "Sending Scheduled scan without filtering\n"); | ||
990 | scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL); | 1010 | scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL); |
991 | } | ||
992 | 1011 | ||
993 | if (mvm->last_ebs_successful && | 1012 | if (mvm->last_ebs_successful && |
994 | mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) | 1013 | mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) |
@@ -1006,12 +1025,19 @@ int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm, | |||
1006 | { | 1025 | { |
1007 | int ret; | 1026 | int ret; |
1008 | 1027 | ||
1009 | if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) { | 1028 | if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) { |
1029 | ret = iwl_mvm_config_sched_scan_profiles(mvm, req); | ||
1030 | if (ret) | ||
1031 | return ret; | ||
1032 | ret = iwl_mvm_sched_scan_umac(mvm, vif, req, ies); | ||
1033 | } else if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) { | ||
1034 | mvm->scan_status = IWL_MVM_SCAN_SCHED; | ||
1010 | ret = iwl_mvm_config_sched_scan_profiles(mvm, req); | 1035 | ret = iwl_mvm_config_sched_scan_profiles(mvm, req); |
1011 | if (ret) | 1036 | if (ret) |
1012 | return ret; | 1037 | return ret; |
1013 | ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies); | 1038 | ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies); |
1014 | } else { | 1039 | } else { |
1040 | mvm->scan_status = IWL_MVM_SCAN_SCHED; | ||
1015 | ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); | 1041 | ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); |
1016 | if (ret) | 1042 | if (ret) |
1017 | return ret; | 1043 | return ret; |
@@ -1068,6 +1094,10 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) | |||
1068 | 1094 | ||
1069 | lockdep_assert_held(&mvm->mutex); | 1095 | lockdep_assert_held(&mvm->mutex); |
1070 | 1096 | ||
1097 | if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) | ||
1098 | return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN, | ||
1099 | notify); | ||
1100 | |||
1071 | if (mvm->scan_status != IWL_MVM_SCAN_SCHED && | 1101 | if (mvm->scan_status != IWL_MVM_SCAN_SCHED && |
1072 | (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) || | 1102 | (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) || |
1073 | mvm->scan_status != IWL_MVM_SCAN_OS)) { | 1103 | mvm->scan_status != IWL_MVM_SCAN_OS)) { |
@@ -1155,20 +1185,64 @@ iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm, | |||
1155 | } | 1185 | } |
1156 | } | 1186 | } |
1157 | 1187 | ||
1188 | static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies, | ||
1189 | size_t len, u8 *const pos) | ||
1190 | { | ||
1191 | static const u8 before_ds_params[] = { | ||
1192 | WLAN_EID_SSID, | ||
1193 | WLAN_EID_SUPP_RATES, | ||
1194 | WLAN_EID_REQUEST, | ||
1195 | WLAN_EID_EXT_SUPP_RATES, | ||
1196 | }; | ||
1197 | size_t offs; | ||
1198 | u8 *newpos = pos; | ||
1199 | |||
1200 | if (!iwl_mvm_rrm_scan_needed(mvm)) { | ||
1201 | memcpy(newpos, ies, len); | ||
1202 | return newpos + len; | ||
1203 | } | ||
1204 | |||
1205 | offs = ieee80211_ie_split(ies, len, | ||
1206 | before_ds_params, | ||
1207 | ARRAY_SIZE(before_ds_params), | ||
1208 | 0); | ||
1209 | |||
1210 | memcpy(newpos, ies, offs); | ||
1211 | newpos += offs; | ||
1212 | |||
1213 | /* Add a placeholder for DS Parameter Set element */ | ||
1214 | *newpos++ = WLAN_EID_DS_PARAMS; | ||
1215 | *newpos++ = 1; | ||
1216 | *newpos++ = 0; | ||
1217 | |||
1218 | memcpy(newpos, ies + offs, len - offs); | ||
1219 | newpos += len - offs; | ||
1220 | |||
1221 | return newpos; | ||
1222 | } | ||
1223 | |||
1158 | static void | 1224 | static void |
1159 | iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 1225 | iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
1160 | struct ieee80211_scan_ies *ies, | 1226 | struct ieee80211_scan_ies *ies, |
1161 | struct iwl_scan_req_unified_lmac *cmd) | 1227 | struct iwl_scan_probe_req *preq, |
1228 | const u8 *mac_addr, const u8 *mac_addr_mask) | ||
1162 | { | 1229 | { |
1163 | struct iwl_scan_probe_req *preq = (void *)(cmd->data + | ||
1164 | sizeof(struct iwl_scan_channel_cfg_lmac) * | ||
1165 | mvm->fw->ucode_capa.n_scan_channels); | ||
1166 | struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf; | 1230 | struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf; |
1167 | u8 *pos; | 1231 | u8 *pos, *newpos; |
1232 | |||
1233 | /* | ||
1234 | * Unfortunately, right now the offload scan doesn't support randomising | ||
1235 | * within the firmware, so until the firmware API is ready we implement | ||
1236 | * it in the driver. This means that the scan iterations won't really be | ||
1237 | * random, only when it's restarted, but at least that helps a bit. | ||
1238 | */ | ||
1239 | if (mac_addr) | ||
1240 | get_random_mask_addr(frame->sa, mac_addr, mac_addr_mask); | ||
1241 | else | ||
1242 | memcpy(frame->sa, vif->addr, ETH_ALEN); | ||
1168 | 1243 | ||
1169 | frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); | 1244 | frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); |
1170 | eth_broadcast_addr(frame->da); | 1245 | eth_broadcast_addr(frame->da); |
1171 | memcpy(frame->sa, vif->addr, ETH_ALEN); | ||
1172 | eth_broadcast_addr(frame->bssid); | 1246 | eth_broadcast_addr(frame->bssid); |
1173 | frame->seq_ctrl = 0; | 1247 | frame->seq_ctrl = 0; |
1174 | 1248 | ||
@@ -1179,11 +1253,14 @@ iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | |||
1179 | preq->mac_header.offset = 0; | 1253 | preq->mac_header.offset = 0; |
1180 | preq->mac_header.len = cpu_to_le16(24 + 2); | 1254 | preq->mac_header.len = cpu_to_le16(24 + 2); |
1181 | 1255 | ||
1182 | memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ], | 1256 | /* Insert ds parameter set element on 2.4 GHz band */ |
1183 | ies->len[IEEE80211_BAND_2GHZ]); | 1257 | newpos = iwl_mvm_copy_and_insert_ds_elem(mvm, |
1258 | ies->ies[IEEE80211_BAND_2GHZ], | ||
1259 | ies->len[IEEE80211_BAND_2GHZ], | ||
1260 | pos); | ||
1184 | preq->band_data[0].offset = cpu_to_le16(pos - preq->buf); | 1261 | preq->band_data[0].offset = cpu_to_le16(pos - preq->buf); |
1185 | preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]); | 1262 | preq->band_data[0].len = cpu_to_le16(newpos - pos); |
1186 | pos += ies->len[IEEE80211_BAND_2GHZ]; | 1263 | pos = newpos; |
1187 | 1264 | ||
1188 | memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ], | 1265 | memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ], |
1189 | ies->len[IEEE80211_BAND_5GHZ]); | 1266 | ies->len[IEEE80211_BAND_5GHZ]); |
@@ -1244,9 +1321,10 @@ int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, | |||
1244 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, | 1321 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, |
1245 | }; | 1322 | }; |
1246 | struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd; | 1323 | struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd; |
1324 | struct iwl_scan_probe_req *preq; | ||
1247 | struct iwl_mvm_scan_params params = {}; | 1325 | struct iwl_mvm_scan_params params = {}; |
1248 | u32 flags; | 1326 | u32 flags; |
1249 | int ssid_bitmap = 0; | 1327 | u32 ssid_bitmap = 0; |
1250 | int ret, i; | 1328 | int ret, i; |
1251 | 1329 | ||
1252 | lockdep_assert_held(&mvm->mutex); | 1330 | lockdep_assert_held(&mvm->mutex); |
@@ -1305,7 +1383,13 @@ int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, | |||
1305 | req->req.n_channels, ssid_bitmap, | 1383 | req->req.n_channels, ssid_bitmap, |
1306 | cmd); | 1384 | cmd); |
1307 | 1385 | ||
1308 | iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd); | 1386 | preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) * |
1387 | mvm->fw->ucode_capa.n_scan_channels); | ||
1388 | |||
1389 | iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, preq, | ||
1390 | req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? | ||
1391 | req->req.mac_addr : NULL, | ||
1392 | req->req.mac_addr_mask); | ||
1309 | 1393 | ||
1310 | ret = iwl_mvm_send_cmd(mvm, &hcmd); | 1394 | ret = iwl_mvm_send_cmd(mvm, &hcmd); |
1311 | if (!ret) { | 1395 | if (!ret) { |
@@ -1338,6 +1422,7 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, | |||
1338 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, | 1422 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, |
1339 | }; | 1423 | }; |
1340 | struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd; | 1424 | struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd; |
1425 | struct iwl_scan_probe_req *preq; | ||
1341 | struct iwl_mvm_scan_params params = {}; | 1426 | struct iwl_mvm_scan_params params = {}; |
1342 | int ret; | 1427 | int ret; |
1343 | u32 flags = 0, ssid_bitmap = 0; | 1428 | u32 flags = 0, ssid_bitmap = 0; |
@@ -1361,15 +1446,8 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, | |||
1361 | 1446 | ||
1362 | cmd->n_channels = (u8)req->n_channels; | 1447 | cmd->n_channels = (u8)req->n_channels; |
1363 | 1448 | ||
1364 | if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { | 1449 | if (iwl_mvm_scan_pass_all(mvm, req)) |
1365 | IWL_DEBUG_SCAN(mvm, | ||
1366 | "Sending scheduled scan with filtering, n_match_sets %d\n", | ||
1367 | req->n_match_sets); | ||
1368 | } else { | ||
1369 | IWL_DEBUG_SCAN(mvm, | ||
1370 | "Sending Scheduled scan without filtering\n"); | ||
1371 | flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; | 1450 | flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; |
1372 | } | ||
1373 | 1451 | ||
1374 | if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0) | 1452 | if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0) |
1375 | flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; | 1453 | flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; |
@@ -1399,7 +1477,13 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, | |||
1399 | iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels, | 1477 | iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels, |
1400 | ssid_bitmap, cmd); | 1478 | ssid_bitmap, cmd); |
1401 | 1479 | ||
1402 | iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd); | 1480 | preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) * |
1481 | mvm->fw->ucode_capa.n_scan_channels); | ||
1482 | |||
1483 | iwl_mvm_build_unified_scan_probe(mvm, vif, ies, preq, | ||
1484 | req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? | ||
1485 | req->mac_addr : NULL, | ||
1486 | req->mac_addr_mask); | ||
1403 | 1487 | ||
1404 | ret = iwl_mvm_send_cmd(mvm, &hcmd); | 1488 | ret = iwl_mvm_send_cmd(mvm, &hcmd); |
1405 | if (!ret) { | 1489 | if (!ret) { |
@@ -1421,6 +1505,10 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, | |||
1421 | 1505 | ||
1422 | int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) | 1506 | int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) |
1423 | { | 1507 | { |
1508 | if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) | ||
1509 | return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_REG_SCAN, | ||
1510 | true); | ||
1511 | |||
1424 | if (mvm->scan_status == IWL_MVM_SCAN_NONE) | 1512 | if (mvm->scan_status == IWL_MVM_SCAN_NONE) |
1425 | return 0; | 1513 | return 0; |
1426 | 1514 | ||
@@ -1435,3 +1523,576 @@ int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) | |||
1435 | return iwl_mvm_scan_offload_stop(mvm, true); | 1523 | return iwl_mvm_scan_offload_stop(mvm, true); |
1436 | return iwl_mvm_cancel_regular_scan(mvm); | 1524 | return iwl_mvm_cancel_regular_scan(mvm); |
1437 | } | 1525 | } |
1526 | |||
1527 | /* UMAC scan API */ | ||
1528 | |||
1529 | struct iwl_umac_scan_done { | ||
1530 | struct iwl_mvm *mvm; | ||
1531 | enum iwl_umac_scan_uid_type type; | ||
1532 | }; | ||
1533 | |||
1534 | static int rate_to_scan_rate_flag(unsigned int rate) | ||
1535 | { | ||
1536 | static const int rate_to_scan_rate[IWL_RATE_COUNT] = { | ||
1537 | [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M, | ||
1538 | [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M, | ||
1539 | [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M, | ||
1540 | [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M, | ||
1541 | [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M, | ||
1542 | [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M, | ||
1543 | [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M, | ||
1544 | [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M, | ||
1545 | [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M, | ||
1546 | [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M, | ||
1547 | [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M, | ||
1548 | [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M, | ||
1549 | }; | ||
1550 | |||
1551 | return rate_to_scan_rate[rate]; | ||
1552 | } | ||
1553 | |||
1554 | static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm) | ||
1555 | { | ||
1556 | struct ieee80211_supported_band *band; | ||
1557 | unsigned int rates = 0; | ||
1558 | int i; | ||
1559 | |||
1560 | band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; | ||
1561 | for (i = 0; i < band->n_bitrates; i++) | ||
1562 | rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); | ||
1563 | band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; | ||
1564 | for (i = 0; i < band->n_bitrates; i++) | ||
1565 | rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); | ||
1566 | |||
1567 | /* Set both basic rates and supported rates */ | ||
1568 | rates |= SCAN_CONFIG_SUPPORTED_RATE(rates); | ||
1569 | |||
1570 | return cpu_to_le32(rates); | ||
1571 | } | ||
1572 | |||
1573 | int iwl_mvm_config_scan(struct iwl_mvm *mvm) | ||
1574 | { | ||
1575 | |||
1576 | struct iwl_scan_config *scan_config; | ||
1577 | struct ieee80211_supported_band *band; | ||
1578 | int num_channels = | ||
1579 | mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + | ||
1580 | mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; | ||
1581 | int ret, i, j = 0, cmd_size, data_size; | ||
1582 | struct iwl_host_cmd cmd = { | ||
1583 | .id = SCAN_CFG_CMD, | ||
1584 | }; | ||
1585 | |||
1586 | if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) | ||
1587 | return -ENOBUFS; | ||
1588 | |||
1589 | cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels; | ||
1590 | |||
1591 | scan_config = kzalloc(cmd_size, GFP_KERNEL); | ||
1592 | if (!scan_config) | ||
1593 | return -ENOMEM; | ||
1594 | |||
1595 | data_size = cmd_size - sizeof(struct iwl_mvm_umac_cmd_hdr); | ||
1596 | scan_config->hdr.size = cpu_to_le16(data_size); | ||
1597 | scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | | ||
1598 | SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | | ||
1599 | SCAN_CONFIG_FLAG_SET_TX_CHAINS | | ||
1600 | SCAN_CONFIG_FLAG_SET_RX_CHAINS | | ||
1601 | SCAN_CONFIG_FLAG_SET_ALL_TIMES | | ||
1602 | SCAN_CONFIG_FLAG_SET_LEGACY_RATES | | ||
1603 | SCAN_CONFIG_FLAG_SET_MAC_ADDR | | ||
1604 | SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| | ||
1605 | SCAN_CONFIG_N_CHANNELS(num_channels)); | ||
1606 | scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant); | ||
1607 | scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); | ||
1608 | scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); | ||
1609 | scan_config->out_of_channel_time = cpu_to_le32(170); | ||
1610 | scan_config->suspend_time = cpu_to_le32(30); | ||
1611 | scan_config->dwell_active = 20; | ||
1612 | scan_config->dwell_passive = 110; | ||
1613 | scan_config->dwell_fragmented = 20; | ||
1614 | |||
1615 | memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); | ||
1616 | |||
1617 | scan_config->bcast_sta_id = mvm->aux_sta.sta_id; | ||
1618 | scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS | | ||
1619 | IWL_CHANNEL_FLAG_ACCURATE_EBS | | ||
1620 | IWL_CHANNEL_FLAG_EBS_ADD | | ||
1621 | IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; | ||
1622 | |||
1623 | band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; | ||
1624 | for (i = 0; i < band->n_channels; i++, j++) | ||
1625 | scan_config->channel_array[j] = band->channels[i].center_freq; | ||
1626 | band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; | ||
1627 | for (i = 0; i < band->n_channels; i++, j++) | ||
1628 | scan_config->channel_array[j] = band->channels[i].center_freq; | ||
1629 | |||
1630 | cmd.data[0] = scan_config; | ||
1631 | cmd.len[0] = cmd_size; | ||
1632 | cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; | ||
1633 | |||
1634 | IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); | ||
1635 | |||
1636 | ret = iwl_mvm_send_cmd(mvm, &cmd); | ||
1637 | |||
1638 | kfree(scan_config); | ||
1639 | return ret; | ||
1640 | } | ||
1641 | |||
1642 | static int iwl_mvm_find_scan_uid(struct iwl_mvm *mvm, u32 uid) | ||
1643 | { | ||
1644 | int i; | ||
1645 | |||
1646 | for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) | ||
1647 | if (mvm->scan_uid[i] == uid) | ||
1648 | return i; | ||
1649 | |||
1650 | return i; | ||
1651 | } | ||
1652 | |||
1653 | static int iwl_mvm_find_free_scan_uid(struct iwl_mvm *mvm) | ||
1654 | { | ||
1655 | return iwl_mvm_find_scan_uid(mvm, 0); | ||
1656 | } | ||
1657 | |||
1658 | static bool iwl_mvm_find_scan_type(struct iwl_mvm *mvm, | ||
1659 | enum iwl_umac_scan_uid_type type) | ||
1660 | { | ||
1661 | int i; | ||
1662 | |||
1663 | for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) | ||
1664 | if (mvm->scan_uid[i] & type) | ||
1665 | return true; | ||
1666 | |||
1667 | return false; | ||
1668 | } | ||
1669 | |||
1670 | static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm, | ||
1671 | enum iwl_umac_scan_uid_type type) | ||
1672 | { | ||
1673 | u32 uid; | ||
1674 | |||
1675 | /* make sure exactly one bit is on in scan type */ | ||
1676 | WARN_ON(hweight8(type) != 1); | ||
1677 | |||
1678 | /* | ||
1679 | * Make sure scan uids are unique. If one scan lasts long time while | ||
1680 | * others are completing frequently, the seq number will wrap up and | ||
1681 | * we may have more than one scan with the same uid. | ||
1682 | */ | ||
1683 | do { | ||
1684 | uid = type | (mvm->scan_seq_num << | ||
1685 | IWL_UMAC_SCAN_UID_SEQ_OFFSET); | ||
1686 | mvm->scan_seq_num++; | ||
1687 | } while (iwl_mvm_find_scan_uid(mvm, uid) < | ||
1688 | IWL_MVM_MAX_SIMULTANEOUS_SCANS); | ||
1689 | |||
1690 | IWL_DEBUG_SCAN(mvm, "Generated scan UID %u\n", uid); | ||
1691 | |||
1692 | return uid; | ||
1693 | } | ||
1694 | |||
1695 | static void | ||
1696 | iwl_mvm_build_generic_umac_scan_cmd(struct iwl_mvm *mvm, | ||
1697 | struct iwl_scan_req_umac *cmd, | ||
1698 | struct iwl_mvm_scan_params *params) | ||
1699 | { | ||
1700 | memset(cmd, 0, ksize(cmd)); | ||
1701 | cmd->hdr.size = cpu_to_le16(iwl_mvm_scan_size(mvm) - | ||
1702 | sizeof(struct iwl_mvm_umac_cmd_hdr)); | ||
1703 | cmd->active_dwell = params->dwell[IEEE80211_BAND_2GHZ].active; | ||
1704 | cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive; | ||
1705 | if (params->passive_fragmented) | ||
1706 | cmd->fragmented_dwell = | ||
1707 | params->dwell[IEEE80211_BAND_2GHZ].passive; | ||
1708 | cmd->max_out_time = cpu_to_le32(params->max_out_time); | ||
1709 | cmd->suspend_time = cpu_to_le32(params->suspend_time); | ||
1710 | cmd->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); | ||
1711 | } | ||
1712 | |||
1713 | static void | ||
1714 | iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, | ||
1715 | struct ieee80211_channel **channels, | ||
1716 | int n_channels, u32 ssid_bitmap, | ||
1717 | struct iwl_scan_req_umac *cmd) | ||
1718 | { | ||
1719 | struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data; | ||
1720 | int i; | ||
1721 | |||
1722 | for (i = 0; i < n_channels; i++) { | ||
1723 | channel_cfg[i].flags = cpu_to_le32(ssid_bitmap); | ||
1724 | channel_cfg[i].channel_num = channels[i]->hw_value; | ||
1725 | channel_cfg[i].iter_count = 1; | ||
1726 | channel_cfg[i].iter_interval = 0; | ||
1727 | } | ||
1728 | } | ||
1729 | |||
1730 | static u32 iwl_mvm_scan_umac_common_flags(struct iwl_mvm *mvm, int n_ssids, | ||
1731 | struct cfg80211_ssid *ssids, | ||
1732 | int fragmented) | ||
1733 | { | ||
1734 | int flags = 0; | ||
1735 | |||
1736 | if (n_ssids == 0) | ||
1737 | flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE; | ||
1738 | |||
1739 | if (n_ssids == 1 && ssids[0].ssid_len != 0) | ||
1740 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT; | ||
1741 | |||
1742 | if (fragmented) | ||
1743 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED; | ||
1744 | |||
1745 | if (iwl_mvm_rrm_scan_needed(mvm)) | ||
1746 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED; | ||
1747 | |||
1748 | return flags; | ||
1749 | } | ||
1750 | |||
1751 | int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | ||
1752 | struct ieee80211_scan_request *req) | ||
1753 | { | ||
1754 | struct iwl_host_cmd hcmd = { | ||
1755 | .id = SCAN_REQ_UMAC, | ||
1756 | .len = { iwl_mvm_scan_size(mvm), }, | ||
1757 | .data = { mvm->scan_cmd, }, | ||
1758 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, | ||
1759 | }; | ||
1760 | struct iwl_scan_req_umac *cmd = mvm->scan_cmd; | ||
1761 | struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data + | ||
1762 | sizeof(struct iwl_scan_channel_cfg_umac) * | ||
1763 | mvm->fw->ucode_capa.n_scan_channels; | ||
1764 | struct iwl_mvm_scan_params params = {}; | ||
1765 | u32 uid, flags; | ||
1766 | u32 ssid_bitmap = 0; | ||
1767 | int ret, i, uid_idx; | ||
1768 | |||
1769 | lockdep_assert_held(&mvm->mutex); | ||
1770 | |||
1771 | uid_idx = iwl_mvm_find_free_scan_uid(mvm); | ||
1772 | if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS) | ||
1773 | return -EBUSY; | ||
1774 | |||
1775 | /* we should have failed registration if scan_cmd was NULL */ | ||
1776 | if (WARN_ON(mvm->scan_cmd == NULL)) | ||
1777 | return -ENOMEM; | ||
1778 | |||
1779 | if (WARN_ON(req->req.n_ssids > PROBE_OPTION_MAX || | ||
1780 | req->ies.common_ie_len + | ||
1781 | req->ies.len[NL80211_BAND_2GHZ] + | ||
1782 | req->ies.len[NL80211_BAND_5GHZ] + 24 + 2 > | ||
1783 | SCAN_OFFLOAD_PROBE_REQ_SIZE || req->req.n_channels > | ||
1784 | mvm->fw->ucode_capa.n_scan_channels)) | ||
1785 | return -ENOBUFS; | ||
1786 | |||
1787 | iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags, | ||
1788 | ¶ms); | ||
1789 | |||
1790 | iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, ¶ms); | ||
1791 | |||
1792 | uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_REG_SCAN); | ||
1793 | mvm->scan_uid[uid_idx] = uid; | ||
1794 | cmd->uid = cpu_to_le32(uid); | ||
1795 | |||
1796 | cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); | ||
1797 | |||
1798 | flags = iwl_mvm_scan_umac_common_flags(mvm, req->req.n_ssids, | ||
1799 | req->req.ssids, | ||
1800 | params.passive_fragmented); | ||
1801 | |||
1802 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL; | ||
1803 | |||
1804 | cmd->general_flags = cpu_to_le32(flags); | ||
1805 | cmd->n_channels = req->req.n_channels; | ||
1806 | |||
1807 | for (i = 0; i < req->req.n_ssids; i++) | ||
1808 | ssid_bitmap |= BIT(i); | ||
1809 | |||
1810 | iwl_mvm_umac_scan_cfg_channels(mvm, req->req.channels, | ||
1811 | req->req.n_channels, ssid_bitmap, cmd); | ||
1812 | |||
1813 | sec_part->schedule[0].iter_count = 1; | ||
1814 | sec_part->delay = 0; | ||
1815 | |||
1816 | iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, &sec_part->preq, | ||
1817 | req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? | ||
1818 | req->req.mac_addr : NULL, | ||
1819 | req->req.mac_addr_mask); | ||
1820 | |||
1821 | iwl_mvm_scan_fill_ssids(sec_part->direct_scan, req->req.ssids, | ||
1822 | req->req.n_ssids, 0); | ||
1823 | |||
1824 | ret = iwl_mvm_send_cmd(mvm, &hcmd); | ||
1825 | if (!ret) { | ||
1826 | IWL_DEBUG_SCAN(mvm, | ||
1827 | "Scan request was sent successfully\n"); | ||
1828 | } else { | ||
1829 | /* | ||
1830 | * If the scan failed, it usually means that the FW was unable | ||
1831 | * to allocate the time events. Warn on it, but maybe we | ||
1832 | * should try to send the command again with different params. | ||
1833 | */ | ||
1834 | IWL_ERR(mvm, "Scan failed! ret %d\n", ret); | ||
1835 | } | ||
1836 | return ret; | ||
1837 | } | ||
1838 | |||
1839 | int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | ||
1840 | struct cfg80211_sched_scan_request *req, | ||
1841 | struct ieee80211_scan_ies *ies) | ||
1842 | { | ||
1843 | |||
1844 | struct iwl_host_cmd hcmd = { | ||
1845 | .id = SCAN_REQ_UMAC, | ||
1846 | .len = { iwl_mvm_scan_size(mvm), }, | ||
1847 | .data = { mvm->scan_cmd, }, | ||
1848 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, | ||
1849 | }; | ||
1850 | struct iwl_scan_req_umac *cmd = mvm->scan_cmd; | ||
1851 | struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data + | ||
1852 | sizeof(struct iwl_scan_channel_cfg_umac) * | ||
1853 | mvm->fw->ucode_capa.n_scan_channels; | ||
1854 | struct iwl_mvm_scan_params params = {}; | ||
1855 | u32 uid, flags; | ||
1856 | u32 ssid_bitmap = 0; | ||
1857 | int ret, uid_idx; | ||
1858 | |||
1859 | lockdep_assert_held(&mvm->mutex); | ||
1860 | |||
1861 | uid_idx = iwl_mvm_find_free_scan_uid(mvm); | ||
1862 | if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS) | ||
1863 | return -EBUSY; | ||
1864 | |||
1865 | /* we should have failed registration if scan_cmd was NULL */ | ||
1866 | if (WARN_ON(mvm->scan_cmd == NULL)) | ||
1867 | return -ENOMEM; | ||
1868 | |||
1869 | if (WARN_ON(req->n_ssids > PROBE_OPTION_MAX || | ||
1870 | ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] + | ||
1871 | ies->len[NL80211_BAND_5GHZ] + 24 + 2 > | ||
1872 | SCAN_OFFLOAD_PROBE_REQ_SIZE || req->n_channels > | ||
1873 | mvm->fw->ucode_capa.n_scan_channels)) | ||
1874 | return -ENOBUFS; | ||
1875 | |||
1876 | iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags, | ||
1877 | ¶ms); | ||
1878 | |||
1879 | iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, ¶ms); | ||
1880 | |||
1881 | cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); | ||
1882 | |||
1883 | uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN); | ||
1884 | mvm->scan_uid[uid_idx] = uid; | ||
1885 | cmd->uid = cpu_to_le32(uid); | ||
1886 | |||
1887 | cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_LOW); | ||
1888 | |||
1889 | flags = iwl_mvm_scan_umac_common_flags(mvm, req->n_ssids, req->ssids, | ||
1890 | params.passive_fragmented); | ||
1891 | |||
1892 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC; | ||
1893 | |||
1894 | if (iwl_mvm_scan_pass_all(mvm, req)) | ||
1895 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL; | ||
1896 | else | ||
1897 | flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH; | ||
1898 | |||
1899 | cmd->general_flags = cpu_to_le32(flags); | ||
1900 | |||
1901 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT && | ||
1902 | mvm->last_ebs_successful) | ||
1903 | cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | | ||
1904 | IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | | ||
1905 | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; | ||
1906 | |||
1907 | cmd->n_channels = req->n_channels; | ||
1908 | |||
1909 | iwl_scan_offload_build_ssid(req, sec_part->direct_scan, &ssid_bitmap, | ||
1910 | false); | ||
1911 | |||
1912 | /* This API uses bits 0-19 instead of 1-20. */ | ||
1913 | ssid_bitmap = ssid_bitmap >> 1; | ||
1914 | |||
1915 | iwl_mvm_umac_scan_cfg_channels(mvm, req->channels, req->n_channels, | ||
1916 | ssid_bitmap, cmd); | ||
1917 | |||
1918 | sec_part->schedule[0].interval = | ||
1919 | cpu_to_le16(req->interval / MSEC_PER_SEC); | ||
1920 | sec_part->schedule[0].iter_count = 0xff; | ||
1921 | |||
1922 | sec_part->delay = 0; | ||
1923 | |||
1924 | iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq, | ||
1925 | req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? | ||
1926 | req->mac_addr : NULL, | ||
1927 | req->mac_addr_mask); | ||
1928 | |||
1929 | ret = iwl_mvm_send_cmd(mvm, &hcmd); | ||
1930 | if (!ret) { | ||
1931 | IWL_DEBUG_SCAN(mvm, | ||
1932 | "Sched scan request was sent successfully\n"); | ||
1933 | } else { | ||
1934 | /* | ||
1935 | * If the scan failed, it usually means that the FW was unable | ||
1936 | * to allocate the time events. Warn on it, but maybe we | ||
1937 | * should try to send the command again with different params. | ||
1938 | */ | ||
1939 | IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret); | ||
1940 | } | ||
1941 | return ret; | ||
1942 | } | ||
1943 | |||
1944 | int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, | ||
1945 | struct iwl_rx_cmd_buffer *rxb, | ||
1946 | struct iwl_device_cmd *cmd) | ||
1947 | { | ||
1948 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
1949 | struct iwl_umac_scan_complete *notif = (void *)pkt->data; | ||
1950 | u32 uid = __le32_to_cpu(notif->uid); | ||
1951 | bool sched = !!(uid & IWL_UMAC_SCAN_UID_SCHED_SCAN); | ||
1952 | int uid_idx = iwl_mvm_find_scan_uid(mvm, uid); | ||
1953 | |||
1954 | /* | ||
1955 | * Scan uid may be set to zero in case of scan abort request from above. | ||
1956 | */ | ||
1957 | if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS) | ||
1958 | return 0; | ||
1959 | |||
1960 | IWL_DEBUG_SCAN(mvm, | ||
1961 | "Scan completed, uid %u type %s, status %s, EBS status %s\n", | ||
1962 | uid, sched ? "sched" : "regular", | ||
1963 | notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? | ||
1964 | "completed" : "aborted", | ||
1965 | notif->ebs_status == IWL_SCAN_EBS_SUCCESS ? | ||
1966 | "success" : "failed"); | ||
1967 | |||
1968 | mvm->last_ebs_successful = !notif->ebs_status; | ||
1969 | mvm->scan_uid[uid_idx] = 0; | ||
1970 | |||
1971 | if (!sched) { | ||
1972 | ieee80211_scan_completed(mvm->hw, | ||
1973 | notif->status == | ||
1974 | IWL_SCAN_OFFLOAD_ABORTED); | ||
1975 | iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); | ||
1976 | } else if (!iwl_mvm_find_scan_type(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN)) { | ||
1977 | ieee80211_sched_scan_stopped(mvm->hw); | ||
1978 | } else { | ||
1979 | IWL_DEBUG_SCAN(mvm, "Another sched scan is running\n"); | ||
1980 | } | ||
1981 | |||
1982 | return 0; | ||
1983 | } | ||
1984 | |||
1985 | static bool iwl_scan_umac_done_check(struct iwl_notif_wait_data *notif_wait, | ||
1986 | struct iwl_rx_packet *pkt, void *data) | ||
1987 | { | ||
1988 | struct iwl_umac_scan_done *scan_done = data; | ||
1989 | struct iwl_umac_scan_complete *notif = (void *)pkt->data; | ||
1990 | u32 uid = __le32_to_cpu(notif->uid); | ||
1991 | int uid_idx = iwl_mvm_find_scan_uid(scan_done->mvm, uid); | ||
1992 | |||
1993 | if (WARN_ON(pkt->hdr.cmd != SCAN_COMPLETE_UMAC)) | ||
1994 | return false; | ||
1995 | |||
1996 | if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS) | ||
1997 | return false; | ||
1998 | |||
1999 | /* | ||
2000 | * Clear scan uid of scans that was aborted from above and completed | ||
2001 | * in FW so the RX handler does nothing. | ||
2002 | */ | ||
2003 | scan_done->mvm->scan_uid[uid_idx] = 0; | ||
2004 | |||
2005 | return !iwl_mvm_find_scan_type(scan_done->mvm, scan_done->type); | ||
2006 | } | ||
2007 | |||
2008 | static int iwl_umac_scan_abort_one(struct iwl_mvm *mvm, u32 uid) | ||
2009 | { | ||
2010 | struct iwl_umac_scan_abort cmd = { | ||
2011 | .hdr.size = cpu_to_le16(sizeof(struct iwl_umac_scan_abort) - | ||
2012 | sizeof(struct iwl_mvm_umac_cmd_hdr)), | ||
2013 | .uid = cpu_to_le32(uid), | ||
2014 | }; | ||
2015 | |||
2016 | lockdep_assert_held(&mvm->mutex); | ||
2017 | |||
2018 | IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); | ||
2019 | |||
2020 | return iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_UMAC, 0, sizeof(cmd), &cmd); | ||
2021 | } | ||
2022 | |||
2023 | static int iwl_umac_scan_stop(struct iwl_mvm *mvm, | ||
2024 | enum iwl_umac_scan_uid_type type, bool notify) | ||
2025 | { | ||
2026 | struct iwl_notification_wait wait_scan_done; | ||
2027 | static const u8 scan_done_notif[] = { SCAN_COMPLETE_UMAC, }; | ||
2028 | struct iwl_umac_scan_done scan_done = { | ||
2029 | .mvm = mvm, | ||
2030 | .type = type, | ||
2031 | }; | ||
2032 | int i, ret = -EIO; | ||
2033 | |||
2034 | iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done, | ||
2035 | scan_done_notif, | ||
2036 | ARRAY_SIZE(scan_done_notif), | ||
2037 | iwl_scan_umac_done_check, &scan_done); | ||
2038 | |||
2039 | IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type); | ||
2040 | |||
2041 | for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) { | ||
2042 | if (mvm->scan_uid[i] & type) { | ||
2043 | int err; | ||
2044 | |||
2045 | if (iwl_mvm_is_radio_killed(mvm) && | ||
2046 | (type & IWL_UMAC_SCAN_UID_REG_SCAN)) { | ||
2047 | ieee80211_scan_completed(mvm->hw, true); | ||
2048 | iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); | ||
2049 | break; | ||
2050 | } | ||
2051 | |||
2052 | err = iwl_umac_scan_abort_one(mvm, mvm->scan_uid[i]); | ||
2053 | if (!err) | ||
2054 | ret = 0; | ||
2055 | } | ||
2056 | } | ||
2057 | |||
2058 | if (ret) { | ||
2059 | IWL_DEBUG_SCAN(mvm, "Couldn't stop scan\n"); | ||
2060 | iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); | ||
2061 | return ret; | ||
2062 | } | ||
2063 | |||
2064 | ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); | ||
2065 | if (ret) | ||
2066 | return ret; | ||
2067 | |||
2068 | if (notify) { | ||
2069 | if (type & IWL_UMAC_SCAN_UID_SCHED_SCAN) | ||
2070 | ieee80211_sched_scan_stopped(mvm->hw); | ||
2071 | if (type & IWL_UMAC_SCAN_UID_REG_SCAN) { | ||
2072 | ieee80211_scan_completed(mvm->hw, true); | ||
2073 | iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); | ||
2074 | } | ||
2075 | } | ||
2076 | |||
2077 | return ret; | ||
2078 | } | ||
2079 | |||
2080 | int iwl_mvm_scan_size(struct iwl_mvm *mvm) | ||
2081 | { | ||
2082 | if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) | ||
2083 | return sizeof(struct iwl_scan_req_umac) + | ||
2084 | sizeof(struct iwl_scan_channel_cfg_umac) * | ||
2085 | mvm->fw->ucode_capa.n_scan_channels + | ||
2086 | sizeof(struct iwl_scan_req_umac_tail); | ||
2087 | |||
2088 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) | ||
2089 | return sizeof(struct iwl_scan_req_unified_lmac) + | ||
2090 | sizeof(struct iwl_scan_channel_cfg_lmac) * | ||
2091 | mvm->fw->ucode_capa.n_scan_channels + | ||
2092 | sizeof(struct iwl_scan_probe_req); | ||
2093 | |||
2094 | return sizeof(struct iwl_scan_cmd) + | ||
2095 | mvm->fw->ucode_capa.max_probe_length + | ||
2096 | mvm->fw->ucode_capa.n_scan_channels * | ||
2097 | sizeof(struct iwl_scan_channel); | ||
2098 | } | ||