summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-12-07 14:10:10 -0500
committerDavid S. Miller <davem@davemloft.net>2015-12-07 14:10:10 -0500
commitad9360b3e5d428d859256390906dff64f2ddc290 (patch)
treebb4efb4a39034bd657f405f4e2583acfc1557bdc
parent4c9668dfe1b410e78a40f8815ea7c50d0f71e1ac (diff)
parent1b894521e60c1b91db1e8ba1278660e5c89f1b5f (diff)
Merge tag 'mac80211-next-for-davem-2015-12-07' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says: ==================== This pull request got a bit bigger than I wanted, due to needing to reshuffle and fix some bugs. I merged mac80211 to get the right base for some of these changes. * new mac80211 API for upcoming driver changes: EOSP handling, key iteration * scan abort changes allowing to cancel an ongoing scan * VHT IBSS 80+80 MHz support * re-enable full AP client state tracking after fixes * various small fixes (that weren't relevant for mac80211) * various cleanups ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c95
-rw-r--r--include/net/cfg80211.h10
-rw-r--r--include/net/mac80211.h56
-rw-r--r--include/uapi/linux/nl80211.h11
-rw-r--r--net/mac80211/cfg.c532
-rw-r--r--net/mac80211/debugfs.c1
-rw-r--r--net/mac80211/ibss.c1
-rw-r--r--net/mac80211/ieee80211_i.h25
-rw-r--r--net/mac80211/key.c56
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/mesh_pathtbl.c4
-rw-r--r--net/mac80211/mlme.c6
-rw-r--r--net/mac80211/offchannel.c829
-rw-r--r--net/mac80211/rx.c3
-rw-r--r--net/mac80211/sta_info.c163
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/trace.h25
-rw-r--r--net/mac80211/tx.c2
-rw-r--r--net/mac80211/util.c7
-rw-r--r--net/wireless/core.h7
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c4
-rw-r--r--net/wireless/lib80211_crypt_tkip.c4
-rw-r--r--net/wireless/nl80211.c62
-rw-r--r--net/wireless/ocb.c3
-rw-r--r--net/wireless/rdev-ops.h51
-rw-r--r--net/wireless/reg.c98
-rw-r--r--net/wireless/trace.h103
-rw-r--r--net/wireless/util.c121
28 files changed, 1328 insertions, 957 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index c00a7daaa4bc..c32889a1e39c 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -495,6 +495,9 @@ struct mac80211_hwsim_data {
const struct ieee80211_regdomain *regd;
struct ieee80211_channel *tmp_chan;
+ struct ieee80211_channel *roc_chan;
+ u32 roc_duration;
+ struct delayed_work roc_start;
struct delayed_work roc_done;
struct delayed_work hw_scan;
struct cfg80211_scan_request *hw_scan_request;
@@ -514,6 +517,7 @@ struct mac80211_hwsim_data {
bool ps_poll_pending;
struct dentry *debugfs;
+ uintptr_t pending_cookie;
struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
@@ -810,6 +814,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb);
struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
+ if (WARN_ON(!txrate))
+ return;
+
if (!netif_running(hwsim_mon))
return;
@@ -960,6 +967,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
unsigned int hwsim_flags = 0;
int i;
struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+ uintptr_t cookie;
if (data->ps != PS_DISABLED)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
@@ -1018,7 +1026,10 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
goto nla_put_failure;
/* We create a cookie to identify this skb */
- if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb))
+ data->pending_cookie++;
+ cookie = data->pending_cookie;
+ info->rate_driver_data[0] = (void *)cookie;
+ if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, cookie))
goto nla_put_failure;
genlmsg_end(skb, msg_head);
@@ -1247,6 +1258,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
{
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *channel;
bool ack;
@@ -1292,6 +1304,22 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
ARRAY_SIZE(txi->control.rates));
txi->rate_driver_data[0] = channel;
+
+ if (skb->len >= 24 + 8 &&
+ ieee80211_is_probe_resp(hdr->frame_control)) {
+ /* fake header transmission time */
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_rate *txrate;
+ u64 ts;
+
+ mgmt = (struct ieee80211_mgmt *)skb->data;
+ txrate = ieee80211_get_tx_rate(hw, txi);
+ ts = mac80211_hwsim_get_tsf_raw();
+ mgmt->u.probe_resp.timestamp =
+ cpu_to_le64(ts + data->tsf_offset +
+ 24 * 8 * 10 / txrate->bitrate);
+ }
+
mac80211_hwsim_monitor_rx(hw, skb, channel);
/* wmediumd mode check */
@@ -1871,7 +1899,8 @@ static void hw_scan_work(struct work_struct *work)
req->channels[hwsim->scan_chan_idx]->center_freq);
hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx];
- if (hwsim->tmp_chan->flags & IEEE80211_CHAN_NO_IR ||
+ if (hwsim->tmp_chan->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR) ||
!req->n_ssids) {
dwell = 120;
} else {
@@ -1987,6 +2016,23 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw,
mutex_unlock(&hwsim->mutex);
}
+static void hw_roc_start(struct work_struct *work)
+{
+ struct mac80211_hwsim_data *hwsim =
+ container_of(work, struct mac80211_hwsim_data, roc_start.work);
+
+ mutex_lock(&hwsim->mutex);
+
+ wiphy_debug(hwsim->hw->wiphy, "hwsim ROC begins\n");
+ hwsim->tmp_chan = hwsim->roc_chan;
+ ieee80211_ready_on_channel(hwsim->hw);
+
+ ieee80211_queue_delayed_work(hwsim->hw, &hwsim->roc_done,
+ msecs_to_jiffies(hwsim->roc_duration));
+
+ mutex_unlock(&hwsim->mutex);
+}
+
static void hw_roc_done(struct work_struct *work)
{
struct mac80211_hwsim_data *hwsim =
@@ -2014,16 +2060,14 @@ static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
return -EBUSY;
}
- hwsim->tmp_chan = chan;
+ hwsim->roc_chan = chan;
+ hwsim->roc_duration = duration;
mutex_unlock(&hwsim->mutex);
wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n",
chan->center_freq, duration);
+ ieee80211_queue_delayed_work(hw, &hwsim->roc_start, HZ/50);
- ieee80211_ready_on_channel(hw);
-
- ieee80211_queue_delayed_work(hw, &hwsim->roc_done,
- msecs_to_jiffies(duration));
return 0;
}
@@ -2031,6 +2075,7 @@ static int mac80211_hwsim_croc(struct ieee80211_hw *hw)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
+ cancel_delayed_work_sync(&hwsim->roc_start);
cancel_delayed_work_sync(&hwsim->roc_done);
mutex_lock(&hwsim->mutex);
@@ -2375,6 +2420,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
}
+ INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
@@ -2411,6 +2457,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
NL80211_FEATURE_STATIC_SMPS |
NL80211_FEATURE_DYNAMIC_SMPS |
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
@@ -2710,7 +2757,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
struct mac80211_hwsim_data *data2;
struct ieee80211_tx_info *txi;
struct hwsim_tx_rate *tx_attempts;
- unsigned long ret_skb_ptr;
+ u64 ret_skb_cookie;
struct sk_buff *skb, *tmp;
const u8 *src;
unsigned int hwsim_flags;
@@ -2728,7 +2775,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
- ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+ ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
data2 = get_hwsim_data_ref_from_addr(src);
if (!data2)
@@ -2736,7 +2783,12 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
/* look for the skb matching the cookie passed back from user */
skb_queue_walk_safe(&data2->pending, skb, tmp) {
- if ((unsigned long)skb == ret_skb_ptr) {
+ u64 skb_cookie;
+
+ txi = IEEE80211_SKB_CB(skb);
+ skb_cookie = (u64)(uintptr_t)txi->rate_driver_data[0];
+
+ if (skb_cookie == ret_skb_cookie) {
skb_unlink(skb, &data2->pending);
found = true;
break;
@@ -2827,10 +2879,25 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
/* A frame is received from user space */
memset(&rx_status, 0, sizeof(rx_status));
- /* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
- * packets?
- */
- rx_status.freq = data2->channel->center_freq;
+ if (info->attrs[HWSIM_ATTR_FREQ]) {
+ /* throw away off-channel packets, but allow both the temporary
+ * ("hw" scan/remain-on-channel) and regular channel, since the
+ * internal datapath also allows this
+ */
+ mutex_lock(&data2->mutex);
+ rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]);
+
+ if (rx_status.freq != data2->channel->center_freq &&
+ (!data2->tmp_chan ||
+ rx_status.freq != data2->tmp_chan->center_freq)) {
+ mutex_unlock(&data2->mutex);
+ goto out;
+ }
+ mutex_unlock(&data2->mutex);
+ } else {
+ rx_status.freq = data2->channel->center_freq;
+ }
+
rx_status.band = data2->channel->band;
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2c7bdb81d30c..9bcaaf7cd15a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2321,6 +2321,8 @@ struct cfg80211_qos_map {
* the driver, and will be valid until passed to cfg80211_scan_done().
* For scan results, call cfg80211_inform_bss(); you can call this outside
* the scan/scan_done bracket too.
+ * @abort_scan: Tell the driver to abort an ongoing scan. The driver shall
+ * indicate the status of the scan through cfg80211_scan_done().
*
* @auth: Request to authenticate with the specified peer
* (invoked with the wireless_dev mutex held)
@@ -2593,6 +2595,7 @@ struct cfg80211_ops {
int (*scan)(struct wiphy *wiphy,
struct cfg80211_scan_request *request);
+ void (*abort_scan)(struct wiphy *wiphy, struct wireless_dev *wdev);
int (*auth)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req);
@@ -5173,8 +5176,11 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
* buffer starts, which may be @ielen if the entire (remainder)
* of the buffer should be used.
*/
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset);
+static inline size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset)
+{
+ return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
/**
* cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 760bc4d5a2cf..7c30faff245f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1321,11 +1321,15 @@ struct ieee80211_channel_switch {
* interface. This flag should be set during interface addition,
* but may be set/cleared as late as authentication to an AP. It is
* only valid for managed/station mode interfaces.
+ * @IEEE80211_VIF_GET_NOA_UPDATE: request to handle NOA attributes
+ * and send P2P_PS notification to the driver if NOA changed, even
+ * this is not pure P2P vif.
*/
enum ieee80211_vif_flags {
IEEE80211_VIF_BEACON_FILTER = BIT(0),
IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1),
IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2),
+ IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
};
/**
@@ -1901,6 +1905,11 @@ struct ieee80211_txq {
* @IEEE80211_HW_BEACON_TX_STATUS: The device/driver provides TX status
* for sent beacons.
*
+ * @IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR: Hardware (or driver) requires that each
+ * station has a unique address, i.e. each station entry can be identified
+ * by just its MAC address; this prevents, for example, the same station
+ * from connecting to two virtual AP interfaces at the same time.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -1936,6 +1945,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_TDLS_WIDER_BW,
IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU,
IEEE80211_HW_BEACON_TX_STATUS,
+ IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -4863,6 +4873,28 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
void ieee80211_sta_eosp(struct ieee80211_sta *pubsta);
/**
+ * ieee80211_send_eosp_nullfunc - ask mac80211 to send NDP with EOSP
+ * @pubsta: the station
+ * @tid: the tid of the NDP
+ *
+ * Sometimes the device understands that it needs to close
+ * the Service Period unexpectedly. This can happen when
+ * sending frames that are filling holes in the BA window.
+ * In this case, the device can ask mac80211 to send a
+ * Nullfunc frame with EOSP set. When that happens, the
+ * driver must have called ieee80211_sta_set_buffered() to
+ * let mac80211 know that there are no buffered frames any
+ * more, otherwise mac80211 will get the more_data bit wrong.
+ * The low level driver must have made sure that the frame
+ * will be sent despite the station being in power-save.
+ * Mac80211 won't call allow_buffered_frames().
+ * Note that calling this function, doesn't exempt the driver
+ * from closing the EOSP properly, it will still have to call
+ * ieee80211_sta_eosp when the NDP is sent.
+ */
+void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
+
+/**
* ieee80211_iter_keys - iterate keys programmed into the device
* @hw: pointer obtained from ieee80211_alloc_hw()
* @vif: virtual interface to iterate, may be %NULL for all
@@ -4890,6 +4922,30 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
void *iter_data);
/**
+ * ieee80211_iter_keys_rcu - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+ * @iter: iterator function that will be called for each key
+ * @iter_data: custom data to pass to the iterator function
+ *
+ * This function can be used to iterate all the keys known to
+ * mac80211, even those that weren't previously programmed into
+ * the device. Note that due to locking reasons, keys of station
+ * in removal process will be skipped.
+ *
+ * This function requires being called in an RCU critical section,
+ * and thus iter must be atomic.
+ */
+void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data);
+
+/**
* ieee80211_iter_chan_contexts_atomic - iterate channel contexts
* @hw: pointre obtained from ieee80211_alloc_hw().
* @iter: iterator function
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 1f0b4cf5dd03..5b7b5ebe7ca8 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -820,6 +820,10 @@
* as an event to indicate changes for devices with wiphy-specific regdom
* management.
*
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1006,6 +1010,8 @@ enum nl80211_commands {
NL80211_CMD_WIPHY_REG_CHANGE,
+ NL80211_CMD_ABORT_SCAN,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1764,8 +1770,9 @@ enum nl80211_commands {
* over all channels.
*
* @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
- * scheduled scan (or a WoWLAN net-detect scan) is started, u32
- * in seconds.
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index da471eef07bb..2d1c4c35186d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1216,16 +1216,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOMEM;
- /*
- * defaults -- if userspace wants something else we'll
- * change it accordingly in sta_apply_parameters()
- */
- if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
- !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
- sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
- }
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
sta->sta.tdls = true;
@@ -1994,6 +1984,11 @@ static int ieee80211_scan(struct wiphy *wiphy,
return ieee80211_request_scan(sdata, req);
}
+static void ieee80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ ieee80211_scan_cancel(wiphy_priv(wiphy));
+}
+
static int
ieee80211_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
@@ -2509,294 +2504,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
return 0;
}
-static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
- struct ieee80211_roc_work *new_roc,
- struct ieee80211_roc_work *cur_roc)
-{
- unsigned long now = jiffies;
- unsigned long remaining = cur_roc->hw_start_time +
- msecs_to_jiffies(cur_roc->duration) -
- now;
-
- if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
- return false;
-
- /* if it doesn't fit entirely, schedule a new one */
- if (new_roc->duration > jiffies_to_msecs(remaining))
- return false;
-
- ieee80211_handle_roc_started(new_roc);
-
- /* add to dependents so we send the expired event properly */
- list_add_tail(&new_roc->list, &cur_roc->dependents);
- return true;
-}
-
-static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
-{
- lockdep_assert_held(&local->mtx);
-
- local->roc_cookie_counter++;
-
- /* wow, you wrapped 64 bits ... more likely a bug */
- if (WARN_ON(local->roc_cookie_counter == 0))
- local->roc_cookie_counter++;
-
- return local->roc_cookie_counter;
-}
-
-static int ieee80211_start_roc_work(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- unsigned int duration, u64 *cookie,
- struct sk_buff *txskb,
- enum ieee80211_roc_type type)
-{
- struct ieee80211_roc_work *roc, *tmp;
- bool queued = false;
- int ret;
-
- lockdep_assert_held(&local->mtx);
-
- if (local->use_chanctx && !local->ops->remain_on_channel)
- return -EOPNOTSUPP;
-
- roc = kzalloc(sizeof(*roc), GFP_KERNEL);
- if (!roc)
- return -ENOMEM;
-
- /*
- * If the duration is zero, then the driver
- * wouldn't actually do anything. Set it to
- * 10 for now.
- *
- * TODO: cancel the off-channel operation
- * when we get the SKB's TX status and
- * the wait time was zero before.
- */
- if (!duration)
- duration = 10;
-
- roc->chan = channel;
- roc->duration = duration;
- roc->req_duration = duration;
- roc->frame = txskb;
- roc->type = type;
- roc->sdata = sdata;
- INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
- INIT_LIST_HEAD(&roc->dependents);
-
- /*
- * cookie is either the roc cookie (for normal roc)
- * or the SKB (for mgmt TX)
- */
- if (!txskb) {
- roc->cookie = ieee80211_mgmt_tx_cookie(local);
- *cookie = roc->cookie;
- } else {
- roc->mgmt_tx_cookie = *cookie;
- }
-
- /* if there's one pending or we're scanning, queue this one */
- if (!list_empty(&local->roc_list) ||
- local->scanning || ieee80211_is_radar_required(local))
- goto out_check_combine;
-
- /* if not HW assist, just queue & schedule work */
- if (!local->ops->remain_on_channel) {
- ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
- goto out_queue;
- }
-
- /* otherwise actually kick it off here (for error handling) */
-
- ret = drv_remain_on_channel(local, sdata, channel, duration, type);
- if (ret) {
- kfree(roc);
- return ret;
- }
-
- roc->started = true;
- goto out_queue;
-
- out_check_combine:
- list_for_each_entry(tmp, &local->roc_list, list) {
- if (tmp->chan != channel || tmp->sdata != sdata)
- continue;
-
- /*
- * Extend this ROC if possible:
- *
- * If it hasn't started yet, just increase the duration
- * and add the new one to the list of dependents.
- * If the type of the new ROC has higher priority, modify the
- * type of the previous one to match that of the new one.
- */
- if (!tmp->started) {
- list_add_tail(&roc->list, &tmp->dependents);
- tmp->duration = max(tmp->duration, roc->duration);
- tmp->type = max(tmp->type, roc->type);
- queued = true;
- break;
- }
-
- /* If it has already started, it's more difficult ... */
- if (local->ops->remain_on_channel) {
- /*
- * In the offloaded ROC case, if it hasn't begun, add
- * this new one to the dependent list to be handled
- * when the master one begins. If it has begun,
- * check if it fits entirely within the existing one,
- * in which case it will just be dependent as well.
- * Otherwise, schedule it by itself.
- */
- if (!tmp->hw_begun) {
- list_add_tail(&roc->list, &tmp->dependents);
- queued = true;
- break;
- }
-
- if (ieee80211_coalesce_started_roc(local, roc, tmp))
- queued = true;
- } else if (del_timer_sync(&tmp->work.timer)) {
- unsigned long new_end;
-
- /*
- * In the software ROC case, cancel the timer, if
- * that fails then the finish work is already
- * queued/pending and thus we queue the new ROC
- * normally, if that succeeds then we can extend
- * the timer duration and TX the frame (if any.)
- */
-
- list_add_tail(&roc->list, &tmp->dependents);
- queued = true;
-
- new_end = jiffies + msecs_to_jiffies(roc->duration);
-
- /* ok, it was started & we canceled timer */
- if (time_after(new_end, tmp->work.timer.expires))
- mod_timer(&tmp->work.timer, new_end);
- else
- add_timer(&tmp->work.timer);
-
- ieee80211_handle_roc_started(roc);
- }
- break;
- }
-
- out_queue:
- if (!queued)
- list_add_tail(&roc->list, &local->roc_list);
-
- return 0;
-}
-
-static int ieee80211_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- struct ieee80211_channel *chan,
- unsigned int duration,
- u64 *cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
- int ret;
-
- mutex_lock(&local->mtx);
- ret = ieee80211_start_roc_work(local, sdata, chan,
- duration, cookie, NULL,
- IEEE80211_ROC_TYPE_NORMAL);
- mutex_unlock(&local->mtx);
-
- return ret;
-}
-
-static int ieee80211_cancel_roc(struct ieee80211_local *local,
- u64 cookie, bool mgmt_tx)
-{
- struct ieee80211_roc_work *roc, *tmp, *found = NULL;
- int ret;
-
- mutex_lock(&local->mtx);
- list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
- struct ieee80211_roc_work *dep, *tmp2;
-
- list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
- if (!mgmt_tx && dep->cookie != cookie)
- continue;
- else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
- continue;
- /* found dependent item -- just remove it */
- list_del(&dep->list);
- mutex_unlock(&local->mtx);
-
- ieee80211_roc_notify_destroy(dep, true);
- return 0;
- }
-
- if (!mgmt_tx && roc->cookie != cookie)
- continue;
- else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
- continue;
-
- found = roc;
- break;
- }
-
- if (!found) {
- mutex_unlock(&local->mtx);
- return -ENOENT;
- }
-
- /*
- * We found the item to cancel, so do that. Note that it
- * may have dependents, which we also cancel (and send
- * the expired signal for.) Not doing so would be quite
- * tricky here, but we may need to fix it later.
- */
-
- if (local->ops->remain_on_channel) {
- if (found->started) {
- ret = drv_cancel_remain_on_channel(local);
- if (WARN_ON_ONCE(ret)) {
- mutex_unlock(&local->mtx);
- return ret;
- }
- }
-
- list_del(&found->list);
-
- if (found->started)
- ieee80211_start_next_roc(local);
- mutex_unlock(&local->mtx);
-
- ieee80211_roc_notify_destroy(found, true);
- } else {
- /* work may be pending so use it all the time */
- found->abort = true;
- ieee80211_queue_delayed_work(&local->hw, &found->work, 0);
-
- mutex_unlock(&local->mtx);
-
- /* work will clean up etc */
- flush_delayed_work(&found->work);
- WARN_ON(!found->to_be_freed);
- kfree(found);
- }
-
- return 0;
-}
-
-static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u64 cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
-
- return ieee80211_cancel_roc(local, cookie, false);
-}
-
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_chan_def *chandef,
@@ -3267,9 +2974,21 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
return err;
}
-static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
- struct sk_buff *skb, u64 *cookie,
- gfp_t gfp)
+u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
+{
+ lockdep_assert_held(&local->mtx);
+
+ local->roc_cookie_counter++;
+
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(local->roc_cookie_counter == 0))
+ local->roc_cookie_counter++;
+
+ return local->roc_cookie_counter;
+}
+
+int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
+ u64 *cookie, gfp_t gfp)
{
unsigned long spin_flags;
struct sk_buff *ack_skb;
@@ -3277,7 +2996,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
ack_skb = skb_copy(skb, gfp);
if (!ack_skb)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
spin_lock_irqsave(&local->ack_status_lock, spin_flags);
id = idr_alloc(&local->ack_status_frames, ack_skb,
@@ -3286,7 +3005,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
if (id < 0) {
kfree_skb(ack_skb);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
IEEE80211_SKB_CB(skb)->ack_frame_id = id;
@@ -3294,204 +3013,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
*cookie = ieee80211_mgmt_tx_cookie(local);
IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
- return ack_skb;
-}
-
-static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct cfg80211_mgmt_tx_params *params,
- u64 *cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb, *ack_skb;
- struct sta_info *sta;
- const struct ieee80211_mgmt *mgmt = (void *)params->buf;
- bool need_offchan = false;
- u32 flags;
- int ret;
- u8 *data;
-
- if (params->dont_wait_for_ack)
- flags = IEEE80211_TX_CTL_NO_ACK;
- else
- flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
- IEEE80211_TX_CTL_REQ_TX_STATUS;
-
- if (params->no_cck)
- flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->vif.bss_conf.ibss_joined)
- need_offchan = true;
- /* fall through */
-#ifdef CONFIG_MAC80211_MESH
- case NL80211_IFTYPE_MESH_POINT:
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- !sdata->u.mesh.mesh_id_len)
- need_offchan = true;
- /* fall through */
-#endif
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_P2P_GO:
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- !ieee80211_vif_is_mesh(&sdata->vif) &&
- !rcu_access_pointer(sdata->bss->beacon))
- need_offchan = true;
- if (!ieee80211_is_action(mgmt->frame_control) ||
- mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
- mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
- mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
- break;
- rcu_read_lock();
- sta = sta_info_get(sdata, mgmt->da);
- rcu_read_unlock();
- if (!sta)
- return -ENOLINK;
- break;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- sdata_lock(sdata);
- if (!sdata->u.mgd.associated ||
- (params->offchan && params->wait &&
- local->ops->remain_on_channel &&
- memcmp(sdata->u.mgd.associated->bssid,
- mgmt->bssid, ETH_ALEN)))
- need_offchan = true;
- sdata_unlock(sdata);
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- need_offchan = true;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- /* configurations requiring offchan cannot work if no channel has been
- * specified
- */
- if (need_offchan && !params->chan)
- return -EI