diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-03-10 23:13:26 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-03-11 14:15:55 -0500 |
commit | 266af4c745952e9bebf687dd68af58df553cb59d (patch) | |
tree | b3bbe8ac763395a4dfcfdd2b46948c9963dbfa46 | |
parent | 808118cb41dfe12a1ac0e35515ac4d91b170bdf9 (diff) |
iwlagn: support off-channel TX
Add support to iwlagn for off-channel TX. The
microcode API for this is a bit strange in that
it uses a hacked-up scan command, so the scan
code needs to change quite a bit to accomodate
that and be able to send it out.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 135 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 87 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-commands.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 12 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-scan.c | 41 |
6 files changed, 238 insertions, 51 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 25fccf9a3001..2003c1d4295f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c | |||
@@ -1115,6 +1115,18 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, | |||
1115 | return added; | 1115 | return added; |
1116 | } | 1116 | } |
1117 | 1117 | ||
1118 | static int iwl_fill_offch_tx(struct iwl_priv *priv, void *data, size_t maxlen) | ||
1119 | { | ||
1120 | struct sk_buff *skb = priv->_agn.offchan_tx_skb; | ||
1121 | |||
1122 | if (skb->len < maxlen) | ||
1123 | maxlen = skb->len; | ||
1124 | |||
1125 | memcpy(data, skb->data, maxlen); | ||
1126 | |||
1127 | return maxlen; | ||
1128 | } | ||
1129 | |||
1118 | int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) | 1130 | int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) |
1119 | { | 1131 | { |
1120 | struct iwl_host_cmd cmd = { | 1132 | struct iwl_host_cmd cmd = { |
@@ -1157,17 +1169,25 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) | |||
1157 | scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; | 1169 | scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; |
1158 | scan->quiet_time = IWL_ACTIVE_QUIET_TIME; | 1170 | scan->quiet_time = IWL_ACTIVE_QUIET_TIME; |
1159 | 1171 | ||
1160 | if (iwl_is_any_associated(priv)) { | 1172 | if (priv->scan_type != IWL_SCAN_OFFCH_TX && |
1173 | iwl_is_any_associated(priv)) { | ||
1161 | u16 interval = 0; | 1174 | u16 interval = 0; |
1162 | u32 extra; | 1175 | u32 extra; |
1163 | u32 suspend_time = 100; | 1176 | u32 suspend_time = 100; |
1164 | u32 scan_suspend_time = 100; | 1177 | u32 scan_suspend_time = 100; |
1165 | 1178 | ||
1166 | IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); | 1179 | IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); |
1167 | if (priv->is_internal_short_scan) | 1180 | switch (priv->scan_type) { |
1181 | case IWL_SCAN_OFFCH_TX: | ||
1182 | WARN_ON(1); | ||
1183 | break; | ||
1184 | case IWL_SCAN_RADIO_RESET: | ||
1168 | interval = 0; | 1185 | interval = 0; |
1169 | else | 1186 | break; |
1187 | case IWL_SCAN_NORMAL: | ||
1170 | interval = vif->bss_conf.beacon_int; | 1188 | interval = vif->bss_conf.beacon_int; |
1189 | break; | ||
1190 | } | ||
1171 | 1191 | ||
1172 | scan->suspend_time = 0; | 1192 | scan->suspend_time = 0; |
1173 | scan->max_out_time = cpu_to_le32(200 * 1024); | 1193 | scan->max_out_time = cpu_to_le32(200 * 1024); |
@@ -1180,29 +1200,41 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) | |||
1180 | scan->suspend_time = cpu_to_le32(scan_suspend_time); | 1200 | scan->suspend_time = cpu_to_le32(scan_suspend_time); |
1181 | IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", | 1201 | IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", |
1182 | scan_suspend_time, interval); | 1202 | scan_suspend_time, interval); |
1203 | } else if (priv->scan_type == IWL_SCAN_OFFCH_TX) { | ||
1204 | scan->suspend_time = 0; | ||
1205 | scan->max_out_time = | ||
1206 | cpu_to_le32(1024 * priv->_agn.offchan_tx_timeout); | ||
1183 | } | 1207 | } |
1184 | 1208 | ||
1185 | if (priv->is_internal_short_scan) { | 1209 | switch (priv->scan_type) { |
1210 | case IWL_SCAN_RADIO_RESET: | ||
1186 | IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); | 1211 | IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); |
1187 | } else if (priv->scan_request->n_ssids) { | 1212 | break; |
1188 | int i, p = 0; | 1213 | case IWL_SCAN_NORMAL: |
1189 | IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); | 1214 | if (priv->scan_request->n_ssids) { |
1190 | for (i = 0; i < priv->scan_request->n_ssids; i++) { | 1215 | int i, p = 0; |
1191 | /* always does wildcard anyway */ | 1216 | IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); |
1192 | if (!priv->scan_request->ssids[i].ssid_len) | 1217 | for (i = 0; i < priv->scan_request->n_ssids; i++) { |
1193 | continue; | 1218 | /* always does wildcard anyway */ |
1194 | scan->direct_scan[p].id = WLAN_EID_SSID; | 1219 | if (!priv->scan_request->ssids[i].ssid_len) |
1195 | scan->direct_scan[p].len = | 1220 | continue; |
1196 | priv->scan_request->ssids[i].ssid_len; | 1221 | scan->direct_scan[p].id = WLAN_EID_SSID; |
1197 | memcpy(scan->direct_scan[p].ssid, | 1222 | scan->direct_scan[p].len = |
1198 | priv->scan_request->ssids[i].ssid, | 1223 | priv->scan_request->ssids[i].ssid_len; |
1199 | priv->scan_request->ssids[i].ssid_len); | 1224 | memcpy(scan->direct_scan[p].ssid, |
1200 | n_probes++; | 1225 | priv->scan_request->ssids[i].ssid, |
1201 | p++; | 1226 | priv->scan_request->ssids[i].ssid_len); |
1202 | } | 1227 | n_probes++; |
1203 | is_active = true; | 1228 | p++; |
1204 | } else | 1229 | } |
1205 | IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); | 1230 | is_active = true; |
1231 | } else | ||
1232 | IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); | ||
1233 | break; | ||
1234 | case IWL_SCAN_OFFCH_TX: | ||
1235 | IWL_DEBUG_SCAN(priv, "Start offchannel TX scan.\n"); | ||
1236 | break; | ||
1237 | } | ||
1206 | 1238 | ||
1207 | scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; | 1239 | scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; |
1208 | scan->tx_cmd.sta_id = ctx->bcast_sta_id; | 1240 | scan->tx_cmd.sta_id = ctx->bcast_sta_id; |
@@ -1300,38 +1332,77 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) | |||
1300 | rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; | 1332 | rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; |
1301 | rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; | 1333 | rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; |
1302 | scan->rx_chain = cpu_to_le16(rx_chain); | 1334 | scan->rx_chain = cpu_to_le16(rx_chain); |
1303 | if (!priv->is_internal_short_scan) { | 1335 | switch (priv->scan_type) { |
1336 | case IWL_SCAN_NORMAL: | ||
1304 | cmd_len = iwl_fill_probe_req(priv, | 1337 | cmd_len = iwl_fill_probe_req(priv, |
1305 | (struct ieee80211_mgmt *)scan->data, | 1338 | (struct ieee80211_mgmt *)scan->data, |
1306 | vif->addr, | 1339 | vif->addr, |
1307 | priv->scan_request->ie, | 1340 | priv->scan_request->ie, |
1308 | priv->scan_request->ie_len, | 1341 | priv->scan_request->ie_len, |
1309 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); | 1342 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); |
1310 | } else { | 1343 | break; |
1344 | case IWL_SCAN_RADIO_RESET: | ||
1311 | /* use bcast addr, will not be transmitted but must be valid */ | 1345 | /* use bcast addr, will not be transmitted but must be valid */ |
1312 | cmd_len = iwl_fill_probe_req(priv, | 1346 | cmd_len = iwl_fill_probe_req(priv, |
1313 | (struct ieee80211_mgmt *)scan->data, | 1347 | (struct ieee80211_mgmt *)scan->data, |
1314 | iwl_bcast_addr, NULL, 0, | 1348 | iwl_bcast_addr, NULL, 0, |
1315 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); | 1349 | IWL_MAX_SCAN_SIZE - sizeof(*scan)); |
1316 | 1350 | break; | |
1351 | case IWL_SCAN_OFFCH_TX: | ||
1352 | cmd_len = iwl_fill_offch_tx(priv, scan->data, | ||
1353 | IWL_MAX_SCAN_SIZE | ||
1354 | - sizeof(*scan) | ||
1355 | - sizeof(struct iwl_scan_channel)); | ||
1356 | scan->scan_flags |= IWL_SCAN_FLAGS_ACTION_FRAME_TX; | ||
1357 | break; | ||
1358 | default: | ||
1359 | BUG(); | ||
1317 | } | 1360 | } |
1318 | scan->tx_cmd.len = cpu_to_le16(cmd_len); | 1361 | scan->tx_cmd.len = cpu_to_le16(cmd_len); |
1319 | 1362 | ||
1320 | scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | | 1363 | scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | |
1321 | RXON_FILTER_BCON_AWARE_MSK); | 1364 | RXON_FILTER_BCON_AWARE_MSK); |
1322 | 1365 | ||
1323 | if (priv->is_internal_short_scan) { | 1366 | switch (priv->scan_type) { |
1367 | case IWL_SCAN_RADIO_RESET: | ||
1324 | scan->channel_count = | 1368 | scan->channel_count = |
1325 | iwl_get_single_channel_for_scan(priv, vif, band, | 1369 | iwl_get_single_channel_for_scan(priv, vif, band, |
1326 | (void *)&scan->data[le16_to_cpu( | 1370 | (void *)&scan->data[cmd_len]); |
1327 | scan->tx_cmd.len)]); | 1371 | break; |
1328 | } else { | 1372 | case IWL_SCAN_NORMAL: |
1329 | scan->channel_count = | 1373 | scan->channel_count = |
1330 | iwl_get_channels_for_scan(priv, vif, band, | 1374 | iwl_get_channels_for_scan(priv, vif, band, |
1331 | is_active, n_probes, | 1375 | is_active, n_probes, |
1332 | (void *)&scan->data[le16_to_cpu( | 1376 | (void *)&scan->data[cmd_len]); |
1333 | scan->tx_cmd.len)]); | 1377 | break; |
1378 | case IWL_SCAN_OFFCH_TX: { | ||
1379 | struct iwl_scan_channel *scan_ch; | ||
1380 | |||
1381 | scan->channel_count = 1; | ||
1382 | |||
1383 | scan_ch = (void *)&scan->data[cmd_len]; | ||
1384 | scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; | ||
1385 | scan_ch->channel = | ||
1386 | cpu_to_le16(priv->_agn.offchan_tx_chan->hw_value); | ||
1387 | scan_ch->active_dwell = | ||
1388 | cpu_to_le16(priv->_agn.offchan_tx_timeout); | ||
1389 | scan_ch->passive_dwell = 0; | ||
1390 | |||
1391 | /* Set txpower levels to defaults */ | ||
1392 | scan_ch->dsp_atten = 110; | ||
1393 | |||
1394 | /* NOTE: if we were doing 6Mb OFDM for scans we'd use | ||
1395 | * power level: | ||
1396 | * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; | ||
1397 | */ | ||
1398 | if (priv->_agn.offchan_tx_chan->band == IEEE80211_BAND_5GHZ) | ||
1399 | scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; | ||
1400 | else | ||
1401 | scan_ch->tx_gain = ((1 << 5) | (5 << 3)); | ||
1402 | } | ||
1403 | break; | ||
1334 | } | 1404 | } |
1405 | |||
1335 | if (scan->channel_count == 0) { | 1406 | if (scan->channel_count == 0) { |
1336 | IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); | 1407 | IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); |
1337 | return -EIO; | 1408 | return -EIO; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 19bb567d1c52..b57fbbf3fb64 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -2937,6 +2937,91 @@ static void iwl_bg_rx_replenish(struct work_struct *data) | |||
2937 | mutex_unlock(&priv->mutex); | 2937 | mutex_unlock(&priv->mutex); |
2938 | } | 2938 | } |
2939 | 2939 | ||
2940 | static int iwl_mac_offchannel_tx(struct ieee80211_hw *hw, struct sk_buff *skb, | ||
2941 | struct ieee80211_channel *chan, | ||
2942 | enum nl80211_channel_type channel_type, | ||
2943 | unsigned int wait) | ||
2944 | { | ||
2945 | struct iwl_priv *priv = hw->priv; | ||
2946 | int ret; | ||
2947 | |||
2948 | /* Not supported if we don't have PAN */ | ||
2949 | if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) { | ||
2950 | ret = -EOPNOTSUPP; | ||
2951 | goto free; | ||
2952 | } | ||
2953 | |||
2954 | /* Not supported on pre-P2P firmware */ | ||
2955 | if (!(priv->contexts[IWL_RXON_CTX_PAN].interface_modes & | ||
2956 | BIT(NL80211_IFTYPE_P2P_CLIENT))) { | ||
2957 | ret = -EOPNOTSUPP; | ||
2958 | goto free; | ||
2959 | } | ||
2960 | |||
2961 | mutex_lock(&priv->mutex); | ||
2962 | |||
2963 | if (!priv->contexts[IWL_RXON_CTX_PAN].is_active) { | ||
2964 | /* | ||
2965 | * If the PAN context is free, use the normal | ||
2966 | * way of doing remain-on-channel offload + TX. | ||
2967 | */ | ||
2968 | ret = 1; | ||
2969 | goto out; | ||
2970 | } | ||
2971 | |||
2972 | /* TODO: queue up if scanning? */ | ||
2973 | if (test_bit(STATUS_SCANNING, &priv->status) || | ||
2974 | priv->_agn.offchan_tx_skb) { | ||
2975 | ret = -EBUSY; | ||
2976 | goto out; | ||
2977 | } | ||
2978 | |||
2979 | /* | ||
2980 | * max_scan_ie_len doesn't include the blank SSID or the header, | ||
2981 | * so need to add that again here. | ||
2982 | */ | ||
2983 | if (skb->len > hw->wiphy->max_scan_ie_len + 24 + 2) { | ||
2984 | ret = -ENOBUFS; | ||
2985 | goto out; | ||
2986 | } | ||
2987 | |||
2988 | priv->_agn.offchan_tx_skb = skb; | ||
2989 | priv->_agn.offchan_tx_timeout = wait; | ||
2990 | priv->_agn.offchan_tx_chan = chan; | ||
2991 | |||
2992 | ret = iwl_scan_initiate(priv, priv->contexts[IWL_RXON_CTX_PAN].vif, | ||
2993 | IWL_SCAN_OFFCH_TX, chan->band); | ||
2994 | if (ret) | ||
2995 | priv->_agn.offchan_tx_skb = NULL; | ||
2996 | out: | ||
2997 | mutex_unlock(&priv->mutex); | ||
2998 | free: | ||
2999 | if (ret < 0) | ||
3000 | kfree_skb(skb); | ||
3001 | |||
3002 | return ret; | ||
3003 | } | ||
3004 | |||
3005 | static int iwl_mac_offchannel_tx_cancel_wait(struct ieee80211_hw *hw) | ||
3006 | { | ||
3007 | struct iwl_priv *priv = hw->priv; | ||
3008 | int ret; | ||
3009 | |||
3010 | mutex_lock(&priv->mutex); | ||
3011 | |||
3012 | if (!priv->_agn.offchan_tx_skb) | ||
3013 | return -EINVAL; | ||
3014 | |||
3015 | priv->_agn.offchan_tx_skb = NULL; | ||
3016 | |||
3017 | ret = iwl_scan_cancel_timeout(priv, 200); | ||
3018 | if (ret) | ||
3019 | ret = -EIO; | ||
3020 | mutex_unlock(&priv->mutex); | ||
3021 | |||
3022 | return ret; | ||
3023 | } | ||
3024 | |||
2940 | /***************************************************************************** | 3025 | /***************************************************************************** |
2941 | * | 3026 | * |
2942 | * mac80211 entry point functions | 3027 | * mac80211 entry point functions |
@@ -3815,6 +3900,8 @@ struct ieee80211_ops iwlagn_hw_ops = { | |||
3815 | .tx_last_beacon = iwl_mac_tx_last_beacon, | 3900 | .tx_last_beacon = iwl_mac_tx_last_beacon, |
3816 | .remain_on_channel = iwl_mac_remain_on_channel, | 3901 | .remain_on_channel = iwl_mac_remain_on_channel, |
3817 | .cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel, | 3902 | .cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel, |
3903 | .offchannel_tx = iwl_mac_offchannel_tx, | ||
3904 | .offchannel_tx_cancel_wait = iwl_mac_offchannel_tx_cancel_wait, | ||
3818 | }; | 3905 | }; |
3819 | 3906 | ||
3820 | static void iwl_hw_detect(struct iwl_priv *priv) | 3907 | static void iwl_hw_detect(struct iwl_priv *priv) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 03cfb74da2bc..ca42ffa63ed7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h | |||
@@ -2964,9 +2964,15 @@ struct iwl3945_scan_cmd { | |||
2964 | u8 data[0]; | 2964 | u8 data[0]; |
2965 | } __packed; | 2965 | } __packed; |
2966 | 2966 | ||
2967 | enum iwl_scan_flags { | ||
2968 | /* BIT(0) currently unused */ | ||
2969 | IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1), | ||
2970 | /* bits 2-7 reserved */ | ||
2971 | }; | ||
2972 | |||
2967 | struct iwl_scan_cmd { | 2973 | struct iwl_scan_cmd { |
2968 | __le16 len; | 2974 | __le16 len; |
2969 | u8 reserved0; | 2975 | u8 scan_flags; /* scan flags: see enum iwl_scan_flags */ |
2970 | u8 channel_count; /* # channels in channel list */ | 2976 | u8 channel_count; /* # channels in channel list */ |
2971 | __le16 quiet_time; /* dwell only this # millisecs on quiet channel | 2977 | __le16 quiet_time; /* dwell only this # millisecs on quiet channel |
2972 | * (only for active scan) */ | 2978 | * (only for active scan) */ |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index af47750f8985..b316d833d9a2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h | |||
@@ -63,6 +63,8 @@ | |||
63 | #ifndef __iwl_core_h__ | 63 | #ifndef __iwl_core_h__ |
64 | #define __iwl_core_h__ | 64 | #define __iwl_core_h__ |
65 | 65 | ||
66 | #include "iwl-dev.h" | ||
67 | |||
66 | /************************ | 68 | /************************ |
67 | * forward declarations * | 69 | * forward declarations * |
68 | ************************/ | 70 | ************************/ |
@@ -551,6 +553,10 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, | |||
551 | struct ieee80211_vif *vif); | 553 | struct ieee80211_vif *vif); |
552 | void iwl_setup_scan_deferred_work(struct iwl_priv *priv); | 554 | void iwl_setup_scan_deferred_work(struct iwl_priv *priv); |
553 | void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); | 555 | void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); |
556 | int __must_check iwl_scan_initiate(struct iwl_priv *priv, | ||
557 | struct ieee80211_vif *vif, | ||
558 | enum iwl_scan_type scan_type, | ||
559 | enum ieee80211_band band); | ||
554 | 560 | ||
555 | /* For faster active scanning, scan will move to the next channel if fewer than | 561 | /* For faster active scanning, scan will move to the next channel if fewer than |
556 | * PLCP_QUIET_THRESH packets are heard on this channel within | 562 | * PLCP_QUIET_THRESH packets are heard on this channel within |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 6a41deba6863..68b953f2bdc7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -1230,6 +1230,12 @@ struct iwl_rxon_context { | |||
1230 | } ht; | 1230 | } ht; |
1231 | }; | 1231 | }; |
1232 | 1232 | ||
1233 | enum iwl_scan_type { | ||
1234 | IWL_SCAN_NORMAL, | ||
1235 | IWL_SCAN_RADIO_RESET, | ||
1236 | IWL_SCAN_OFFCH_TX, | ||
1237 | }; | ||
1238 | |||
1233 | struct iwl_priv { | 1239 | struct iwl_priv { |
1234 | 1240 | ||
1235 | /* ieee device used by generic ieee processing code */ | 1241 | /* ieee device used by generic ieee processing code */ |
@@ -1290,7 +1296,7 @@ struct iwl_priv { | |||
1290 | enum ieee80211_band scan_band; | 1296 | enum ieee80211_band scan_band; |
1291 | struct cfg80211_scan_request *scan_request; | 1297 | struct cfg80211_scan_request *scan_request; |
1292 | struct ieee80211_vif *scan_vif; | 1298 | struct ieee80211_vif *scan_vif; |
1293 | bool is_internal_short_scan; | 1299 | enum iwl_scan_type scan_type; |
1294 | u8 scan_tx_ant[IEEE80211_NUM_BANDS]; | 1300 | u8 scan_tx_ant[IEEE80211_NUM_BANDS]; |
1295 | u8 mgmt_tx_ant; | 1301 | u8 mgmt_tx_ant; |
1296 | 1302 | ||
@@ -1504,6 +1510,10 @@ struct iwl_priv { | |||
1504 | struct delayed_work hw_roc_work; | 1510 | struct delayed_work hw_roc_work; |
1505 | enum nl80211_channel_type hw_roc_chantype; | 1511 | enum nl80211_channel_type hw_roc_chantype; |
1506 | int hw_roc_duration; | 1512 | int hw_roc_duration; |
1513 | |||
1514 | struct sk_buff *offchan_tx_skb; | ||
1515 | int offchan_tx_timeout; | ||
1516 | struct ieee80211_channel *offchan_tx_chan; | ||
1507 | } _agn; | 1517 | } _agn; |
1508 | #endif | 1518 | #endif |
1509 | }; | 1519 | }; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index faa6d34cb658..3a4d9e6b0421 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c | |||
@@ -101,7 +101,7 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) | |||
101 | ieee80211_scan_completed(priv->hw, aborted); | 101 | ieee80211_scan_completed(priv->hw, aborted); |
102 | } | 102 | } |
103 | 103 | ||
104 | priv->is_internal_short_scan = false; | 104 | priv->scan_type = IWL_SCAN_NORMAL; |
105 | priv->scan_vif = NULL; | 105 | priv->scan_vif = NULL; |
106 | priv->scan_request = NULL; | 106 | priv->scan_request = NULL; |
107 | } | 107 | } |
@@ -339,10 +339,10 @@ void iwl_init_scan_params(struct iwl_priv *priv) | |||
339 | priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; | 339 | priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; |
340 | } | 340 | } |
341 | 341 | ||
342 | static int __must_check iwl_scan_initiate(struct iwl_priv *priv, | 342 | int __must_check iwl_scan_initiate(struct iwl_priv *priv, |
343 | struct ieee80211_vif *vif, | 343 | struct ieee80211_vif *vif, |
344 | bool internal, | 344 | enum iwl_scan_type scan_type, |
345 | enum ieee80211_band band) | 345 | enum ieee80211_band band) |
346 | { | 346 | { |
347 | int ret; | 347 | int ret; |
348 | 348 | ||
@@ -370,17 +370,19 @@ static int __must_check iwl_scan_initiate(struct iwl_priv *priv, | |||
370 | } | 370 | } |
371 | 371 | ||
372 | IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", | 372 | IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", |
373 | internal ? "internal short " : ""); | 373 | scan_type == IWL_SCAN_NORMAL ? "" : |
374 | scan_type == IWL_SCAN_OFFCH_TX ? "offchan TX " : | ||
375 | "internal short "); | ||
374 | 376 | ||
375 | set_bit(STATUS_SCANNING, &priv->status); | 377 | set_bit(STATUS_SCANNING, &priv->status); |
376 | priv->is_internal_short_scan = internal; | 378 | priv->scan_type = scan_type; |
377 | priv->scan_start = jiffies; | 379 | priv->scan_start = jiffies; |
378 | priv->scan_band = band; | 380 | priv->scan_band = band; |
379 | 381 | ||
380 | ret = priv->cfg->ops->utils->request_scan(priv, vif); | 382 | ret = priv->cfg->ops->utils->request_scan(priv, vif); |
381 | if (ret) { | 383 | if (ret) { |
382 | clear_bit(STATUS_SCANNING, &priv->status); | 384 | clear_bit(STATUS_SCANNING, &priv->status); |
383 | priv->is_internal_short_scan = false; | 385 | priv->scan_type = IWL_SCAN_NORMAL; |
384 | return ret; | 386 | return ret; |
385 | } | 387 | } |
386 | 388 | ||
@@ -405,7 +407,7 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, | |||
405 | mutex_lock(&priv->mutex); | 407 | mutex_lock(&priv->mutex); |
406 | 408 | ||
407 | if (test_bit(STATUS_SCANNING, &priv->status) && | 409 | if (test_bit(STATUS_SCANNING, &priv->status) && |
408 | !priv->is_internal_short_scan) { | 410 | priv->scan_type != IWL_SCAN_NORMAL) { |
409 | IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); | 411 | IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); |
410 | ret = -EAGAIN; | 412 | ret = -EAGAIN; |
411 | goto out_unlock; | 413 | goto out_unlock; |
@@ -419,11 +421,11 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, | |||
419 | * If an internal scan is in progress, just set | 421 | * If an internal scan is in progress, just set |
420 | * up the scan_request as per above. | 422 | * up the scan_request as per above. |
421 | */ | 423 | */ |
422 | if (priv->is_internal_short_scan) { | 424 | if (priv->scan_type != IWL_SCAN_NORMAL) { |
423 | IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n"); | 425 | IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n"); |
424 | ret = 0; | 426 | ret = 0; |
425 | } else | 427 | } else |
426 | ret = iwl_scan_initiate(priv, vif, false, | 428 | ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, |
427 | req->channels[0]->band); | 429 | req->channels[0]->band); |
428 | 430 | ||
429 | IWL_DEBUG_MAC80211(priv, "leave\n"); | 431 | IWL_DEBUG_MAC80211(priv, "leave\n"); |
@@ -452,7 +454,7 @@ static void iwl_bg_start_internal_scan(struct work_struct *work) | |||
452 | 454 | ||
453 | mutex_lock(&priv->mutex); | 455 | mutex_lock(&priv->mutex); |
454 | 456 | ||
455 | if (priv->is_internal_short_scan == true) { | 457 | if (priv->scan_type == IWL_SCAN_RADIO_RESET) { |
456 | IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); | 458 | IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); |
457 | goto unlock; | 459 | goto unlock; |
458 | } | 460 | } |
@@ -462,7 +464,7 @@ static void iwl_bg_start_internal_scan(struct work_struct *work) | |||
462 | goto unlock; | 464 | goto unlock; |
463 | } | 465 | } |
464 | 466 | ||
465 | if (iwl_scan_initiate(priv, NULL, true, priv->band)) | 467 | if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) |
466 | IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); | 468 | IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); |
467 | unlock: | 469 | unlock: |
468 | mutex_unlock(&priv->mutex); | 470 | mutex_unlock(&priv->mutex); |
@@ -549,8 +551,7 @@ static void iwl_bg_scan_completed(struct work_struct *work) | |||
549 | container_of(work, struct iwl_priv, scan_completed); | 551 | container_of(work, struct iwl_priv, scan_completed); |
550 | bool aborted; | 552 | bool aborted; |
551 | 553 | ||
552 | IWL_DEBUG_SCAN(priv, "Completed %sscan.\n", | 554 | IWL_DEBUG_SCAN(priv, "Completed scan.\n"); |
553 | priv->is_internal_short_scan ? "internal short " : ""); | ||
554 | 555 | ||
555 | cancel_delayed_work(&priv->scan_check); | 556 | cancel_delayed_work(&priv->scan_check); |
556 | 557 | ||
@@ -565,7 +566,13 @@ static void iwl_bg_scan_completed(struct work_struct *work) | |||
565 | goto out_settings; | 566 | goto out_settings; |
566 | } | 567 | } |
567 | 568 | ||
568 | if (priv->is_internal_short_scan && !aborted) { | 569 | if (priv->scan_type == IWL_SCAN_OFFCH_TX && priv->_agn.offchan_tx_skb) { |
570 | ieee80211_tx_status_irqsafe(priv->hw, | ||
571 | priv->_agn.offchan_tx_skb); | ||
572 | priv->_agn.offchan_tx_skb = NULL; | ||
573 | } | ||
574 | |||
575 | if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { | ||
569 | int err; | 576 | int err; |
570 | 577 | ||
571 | /* Check if mac80211 requested scan during our internal scan */ | 578 | /* Check if mac80211 requested scan during our internal scan */ |
@@ -573,7 +580,7 @@ static void iwl_bg_scan_completed(struct work_struct *work) | |||
573 | goto out_complete; | 580 | goto out_complete; |
574 | 581 | ||
575 | /* If so request a new scan */ | 582 | /* If so request a new scan */ |
576 | err = iwl_scan_initiate(priv, priv->scan_vif, false, | 583 | err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, |
577 | priv->scan_request->channels[0]->band); | 584 | priv->scan_request->channels[0]->band); |
578 | if (err) { | 585 | if (err) { |
579 | IWL_DEBUG_SCAN(priv, | 586 | IWL_DEBUG_SCAN(priv, |