Skip to content

Commit 266af4c

Browse files
jmberg-intellinvjw
authored andcommitted
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>
1 parent 808118c commit 266af4c

File tree

6 files changed

+238
-51
lines changed

6 files changed

+238
-51
lines changed

drivers/net/wireless/iwlwifi/iwl-agn-lib.c

Lines changed: 103 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,18 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
11151115
return added;
11161116
}
11171117

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+
11181130
int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
11191131
{
11201132
struct iwl_host_cmd cmd = {
@@ -1157,17 +1169,25 @@ int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
11571169
scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
11581170
scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
11591171

1160-
if (iwl_is_any_associated(priv)) {
1172+
if (priv->scan_type != IWL_SCAN_OFFCH_TX &&
1173+
iwl_is_any_associated(priv)) {
11611174
u16 interval = 0;
11621175
u32 extra;
11631176
u32 suspend_time = 100;
11641177
u32 scan_suspend_time = 100;
11651178

11661179
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:
11681185
interval = 0;
1169-
else
1186+
break;
1187+
case IWL_SCAN_NORMAL:
11701188
interval = vif->bss_conf.beacon_int;
1189+
break;
1190+
}
11711191

11721192
scan->suspend_time = 0;
11731193
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)
11801200
scan->suspend_time = cpu_to_le32(scan_suspend_time);
11811201
IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
11821202
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);
11831207
}
11841208

1185-
if (priv->is_internal_short_scan) {
1209+
switch (priv->scan_type) {
1210+
case IWL_SCAN_RADIO_RESET:
11861211
IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
1187-
} else if (priv->scan_request->n_ssids) {
1188-
int i, p = 0;
1189-
IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
1190-
for (i = 0; i < priv->scan_request->n_ssids; i++) {
1191-
/* always does wildcard anyway */
1192-
if (!priv->scan_request->ssids[i].ssid_len)
1193-
continue;
1194-
scan->direct_scan[p].id = WLAN_EID_SSID;
1195-
scan->direct_scan[p].len =
1196-
priv->scan_request->ssids[i].ssid_len;
1197-
memcpy(scan->direct_scan[p].ssid,
1198-
priv->scan_request->ssids[i].ssid,
1199-
priv->scan_request->ssids[i].ssid_len);
1200-
n_probes++;
1201-
p++;
1202-
}
1203-
is_active = true;
1204-
} else
1205-
IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
1212+
break;
1213+
case IWL_SCAN_NORMAL:
1214+
if (priv->scan_request->n_ssids) {
1215+
int i, p = 0;
1216+
IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
1217+
for (i = 0; i < priv->scan_request->n_ssids; i++) {
1218+
/* always does wildcard anyway */
1219+
if (!priv->scan_request->ssids[i].ssid_len)
1220+
continue;
1221+
scan->direct_scan[p].id = WLAN_EID_SSID;
1222+
scan->direct_scan[p].len =
1223+
priv->scan_request->ssids[i].ssid_len;
1224+
memcpy(scan->direct_scan[p].ssid,
1225+
priv->scan_request->ssids[i].ssid,
1226+
priv->scan_request->ssids[i].ssid_len);
1227+
n_probes++;
1228+
p++;
1229+
}
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+
}
12061238

12071239
scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
12081240
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)
13001332
rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
13011333
rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
13021334
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:
13041337
cmd_len = iwl_fill_probe_req(priv,
13051338
(struct ieee80211_mgmt *)scan->data,
13061339
vif->addr,
13071340
priv->scan_request->ie,
13081341
priv->scan_request->ie_len,
13091342
IWL_MAX_SCAN_SIZE - sizeof(*scan));
1310-
} else {
1343+
break;
1344+
case IWL_SCAN_RADIO_RESET:
13111345
/* use bcast addr, will not be transmitted but must be valid */
13121346
cmd_len = iwl_fill_probe_req(priv,
13131347
(struct ieee80211_mgmt *)scan->data,
13141348
iwl_bcast_addr, NULL, 0,
13151349
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();
13171360
}
13181361
scan->tx_cmd.len = cpu_to_le16(cmd_len);
13191362

13201363
scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
13211364
RXON_FILTER_BCON_AWARE_MSK);
13221365

1323-
if (priv->is_internal_short_scan) {
1366+
switch (priv->scan_type) {
1367+
case IWL_SCAN_RADIO_RESET:
13241368
scan->channel_count =
13251369
iwl_get_single_channel_for_scan(priv, vif, band,
1326-
(void *)&scan->data[le16_to_cpu(
1327-
scan->tx_cmd.len)]);
1328-
} else {
1370+
(void *)&scan->data[cmd_len]);
1371+
break;
1372+
case IWL_SCAN_NORMAL:
13291373
scan->channel_count =
13301374
iwl_get_channels_for_scan(priv, vif, band,
13311375
is_active, n_probes,
1332-
(void *)&scan->data[le16_to_cpu(
1333-
scan->tx_cmd.len)]);
1376+
(void *)&scan->data[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;
13341404
}
1405+
13351406
if (scan->channel_count == 0) {
13361407
IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
13371408
return -EIO;

drivers/net/wireless/iwlwifi/iwl-agn.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2937,6 +2937,91 @@ static void iwl_bg_rx_replenish(struct work_struct *data)
29372937
mutex_unlock(&priv->mutex);
29382938
}
29392939

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+
29403025
/*****************************************************************************
29413026
*
29423027
* mac80211 entry point functions
@@ -3815,6 +3900,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
38153900
.tx_last_beacon = iwl_mac_tx_last_beacon,
38163901
.remain_on_channel = iwl_mac_remain_on_channel,
38173902
.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,
38183905
};
38193906

38203907
static void iwl_hw_detect(struct iwl_priv *priv)

drivers/net/wireless/iwlwifi/iwl-commands.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2964,9 +2964,15 @@ struct iwl3945_scan_cmd {
29642964
u8 data[0];
29652965
} __packed;
29662966

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+
29672973
struct iwl_scan_cmd {
29682974
__le16 len;
2969-
u8 reserved0;
2975+
u8 scan_flags;/* scan flags: see enum iwl_scan_flags */
29702976
u8 channel_count;/* # channels in channel list */
29712977
__le16 quiet_time;/* dwell only this # millisecs on quiet channel
29722978
* (only for active scan) */

drivers/net/wireless/iwlwifi/iwl-core.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
#ifndef __iwl_core_h__
6464
#define __iwl_core_h__
6565

66+
#include "iwl-dev.h"
67+
6668
/************************
6769
* forward declarations *
6870
************************/
@@ -551,6 +553,10 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
551553
struct ieee80211_vif *vif);
552554
void iwl_setup_scan_deferred_work(struct iwl_priv *priv);
553555
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);
554560

555561
/* For faster active scanning, scan will move to the next channel if fewer than
556562
* PLCP_QUIET_THRESH packets are heard on this channel within

drivers/net/wireless/iwlwifi/iwl-dev.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,12 @@ struct iwl_rxon_context {
12301230
} ht;
12311231
};
12321232

1233+
enum iwl_scan_type {
1234+
IWL_SCAN_NORMAL,
1235+
IWL_SCAN_RADIO_RESET,
1236+
IWL_SCAN_OFFCH_TX,
1237+
};
1238+
12331239
struct iwl_priv {
12341240

12351241
/* ieee device used by generic ieee processing code */
@@ -1290,7 +1296,7 @@ struct iwl_priv {
12901296
enum ieee80211_band scan_band;
12911297
struct cfg80211_scan_request *scan_request;
12921298
struct ieee80211_vif *scan_vif;
1293-
bool is_internal_short_scan;
1299+
enum iwl_scan_type scan_type;
12941300
u8 scan_tx_ant[IEEE80211_NUM_BANDS];
12951301
u8 mgmt_tx_ant;
12961302

@@ -1504,6 +1510,10 @@ struct iwl_priv {
15041510
struct delayed_work hw_roc_work;
15051511
enum nl80211_channel_type hw_roc_chantype;
15061512
int hw_roc_duration;
1513+
1514+
struct sk_buff *offchan_tx_skb;
1515+
int offchan_tx_timeout;
1516+
struct ieee80211_channel *offchan_tx_chan;
15071517
} _agn;
15081518
#endif
15091519
};

0 commit comments

Comments
 (0)