summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/nl80211.h16
-rw-r--r--include/net/cfg80211.h11
-rw-r--r--net/mac80211/ieee80211_i.h6
-rw-r--r--net/mac80211/scan.c50
-rw-r--r--net/wireless/nl80211.c7
5 files changed, 80 insertions, 10 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 8ad70dcac3f9..bec6bcacc899 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -967,6 +967,8 @@ enum nl80211_commands {
* @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
* for configuration as RX antennas via the above parameters.
*
+ * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
+ *
* @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
*
* @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
@@ -1206,6 +1208,8 @@ enum nl80211_attrs {
NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+ NL80211_ATTR_SCAN_FLAGS,
+
NL80211_ATTR_SUPPORT_MESH_AUTH,
NL80211_ATTR_STA_PLINK_STATE,
@@ -2207,6 +2211,18 @@ enum nl80211_tx_power_setting {
};
/**
+ * enum nl80211_scan_flags - scan request control flags
+ *
+ * Scan request control flags are used to control the handling
+ * of NL80211_CMD_TRIGGER_SCAN, requests.
+ *
+ * @NL80211_SCAN_FLAG_TX_ABORT: abort scan if tx collides
+ */
+enum nl80211_scan_flags {
+ NL80211_SCAN_FLAG_TX_ABORT = 1<<0,
+};
+
+/**
* enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
* @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
* @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index dc4ae1277e47..251a2f936727 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -784,6 +784,15 @@ struct cfg80211_ssid {
};
/**
+ * enum cfg80211_scan_flag - scan request control flags
+ *
+ * @CFG80211_SCAN__FLAG_TX_ABORT: abort scan on pending transmit
+ */
+enum cfg80211_scan_flags {
+ CFG80211_SCAN_FLAG_TX_ABORT = NL80211_SCAN_FLAG_TX_ABORT,
+};
+
+/**
* struct cfg80211_scan_request - scan request description
*
* @ssids: SSIDs to scan for (active scan only)
@@ -792,6 +801,7 @@ struct cfg80211_ssid {
* @n_channels: total number of channels to scan
* @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets
+ * @flags: bit field of flags controlling operation
* @rates: bitmap of rates to advertise for each band
* @wiphy: the wiphy this was for
* @dev: the interface
@@ -803,6 +813,7 @@ struct cfg80211_scan_request {
u32 n_channels;
const u8 *ie;
size_t ie_len;
+ u32 flags;
u32 rates[IEEE80211_NUM_BANDS];
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9fab144cc9f5..8624f27441f2 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -718,6 +718,10 @@ enum {
* about us leaving the channel and stop all associated STA interfaces
* @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the
* AP about us being back and restart all associated STA interfaces
+ * @SCAN_ABORT: Abnormally terminate the scan operation, set only when
+ * on the operating channel
+ * @SCAN_ENTER_OPER_CHANNEL_ABORT: Return to the operating channel then
+ * terminate the scan operation
*/
enum mac80211_scan_state {
SCAN_DECISION,
@@ -725,6 +729,8 @@ enum mac80211_scan_state {
SCAN_SEND_PROBE,
SCAN_LEAVE_OPER_CHANNEL,
SCAN_ENTER_OPER_CHANNEL,
+ SCAN_ABORT,
+ SCAN_ENTER_OPER_CHANNEL_ABORT,
};
struct ieee80211_local {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 6f09eca01112..4aa2cec04f31 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -477,6 +477,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
unsigned long min_beacon_int = 0;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
+ enum mac80211_scan_state next_scan_state;
/*
* check if at least one STA interface is associated,
@@ -512,13 +513,21 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
/* We're currently on operating channel. */
if (next_chan == local->oper_channel)
/* We don't need to move off of operating channel. */
- local->next_scan_state = SCAN_SET_CHANNEL;
- else
+ next_scan_state = SCAN_SET_CHANNEL;
+ else {
/*
* We do need to leave operating channel, as next
- * scan is somewhere else.
+ * scan is somewhere else, unless
+ * there is pending traffic and the scan request is
+ * marked to abort when this happens
*/
- local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
+ if (associated && !tx_empty &&
+ (local->scan_req->flags &
+ CFG80211_SCAN_FLAG_TX_ABORT))
+ next_scan_state = SCAN_ABORT;
+ else
+ next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
+ }
} else {
/*
* we're currently scanning a different channel, let's
@@ -547,12 +556,24 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
usecs_to_jiffies(min_beacon_int * 1024) *
local->hw.conf.listen_interval);
- if (associated && ( !tx_empty || bad_latency ||
- listen_int_exceeded))
- local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
+ if (associated && !tx_empty) {
+ if (unlikely(local->scan_req->flags &
+ CFG80211_SCAN_FLAG_TX_ABORT)) {
+ /*
+ * Scan request is marked to abort when there
+ * is outbound traffic. Mark state to return
+ * the operating channel and then abort. This
+ * happens as soon as possible.
+ */
+ next_scan_state = SCAN_ENTER_OPER_CHANNEL_ABORT;
+ } else
+ next_scan_state = SCAN_ENTER_OPER_CHANNEL;
+ } else if (associated && (bad_latency || listen_int_exceeded))
+ next_scan_state = SCAN_ENTER_OPER_CHANNEL;
else
- local->next_scan_state = SCAN_SET_CHANNEL;
+ next_scan_state = SCAN_SET_CHANNEL;
}
+ local->next_scan_state = next_scan_state;
*next_delay = 0;
}
@@ -596,8 +617,13 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca
*/
ieee80211_offchannel_return(local, true, false);
- *next_delay = HZ / 5;
- local->next_scan_state = SCAN_DECISION;
+ if (local->next_scan_state == SCAN_ENTER_OPER_CHANNEL) {
+ *next_delay = HZ / 5;
+ local->next_scan_state = SCAN_DECISION;
+ } else {
+ *next_delay = 0;
+ local->next_scan_state = SCAN_ABORT;
+ }
}
static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
@@ -741,11 +767,15 @@ void ieee80211_scan_work(struct work_struct *work)
ieee80211_scan_state_send_probe(local, &next_delay);
break;
case SCAN_LEAVE_OPER_CHANNEL:
+ case SCAN_ENTER_OPER_CHANNEL_ABORT:
ieee80211_scan_state_leave_oper_channel(local, &next_delay);
break;
case SCAN_ENTER_OPER_CHANNEL:
ieee80211_scan_state_enter_oper_channel(local, &next_delay);
break;
+ case SCAN_ABORT:
+ aborted = true;
+ goto out_complete;
}
} while (next_delay == 0);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d7b91a351f84..84400e3a17b6 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -131,6 +131,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
[NL80211_ATTR_IE] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
@@ -3457,6 +3458,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->ie_len);
}
+ if (info->attrs[NL80211_ATTR_SCAN_FLAGS])
+ request->flags = nla_get_u32(
+ info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
if (wiphy->bands[i])
request->rates[i] =
@@ -6158,6 +6163,8 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
if (req->ie)
NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
+ NLA_PUT_U8(msg, NL80211_ATTR_SCAN_FLAGS, req->flags);
+
return 0;
nla_put_failure:
return -ENOBUFS;