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
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCgAGBQJWZVw7AAoJEGt7eEactAAdQcgP/1bOBBKgCHWZ8xhqmhLIPPUP
 AgkkyBcjCbSOWyE1axm5WQZM+fQvyGAcYsnhsK7h0Wy5Jvv6goNYhxkoD3L5lAKC
 LkiiqokTpLx1Em6Iugn1sdgag8q7EquYYQN+hOEOWtp32pTsx3/pDglCtGu0SX1N
 eystHEAu6mzPezat99M4s80fRlfBop3yaUuL5XopQFGtU37zfUgoXJB3BoXgxNjK
 XyD22jtPDreDMndZ9ugfvMaiq3iKRBhKXqgGb3SqMaStIyRK8zAkHb5jg3CllMeq
 bEsz4Rb4r+vtm2AVsUMWjfd/upQKwPwuvdvCvv4AQCO+aR9Rm+tR/wnnD4Gtnek5
 zPQ6XWt/0V4CKGl+W9shnDSA1DZ3hTijJlaGsK+RUqEtdq903lEP7fc2GsSvlund
 jXHfOExieuZOToKWTKpmNGsCw6fjJaGXNd/iLWo5VGAZS2X+JLmFZ94g43a6zOGZ
 s1Gz4F3tz4u4Bd26NAK2Z6CQRvDS4OOyLIjl9vpB9Fk/9nQx3f7WD8aBTRuCVAtG
 U2sFEUscz3rkdct30Gvkjm3ovmgc4pomTDvOpmNIsSCi2ygzGWHbEvSrrHdIjzVy
 KDcvRs6bRtCL/WxaaEIk46M6+6aKlSnZytPLl7vkNnvxXuEF7GYdnNVSUbSH9Nte
 XzT4+rZRiqyPZEGhBekw
 =+5dd
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
David S. Miller 2015-12-07 14:10:10 -05:00
commit ad9360b3e5
28 changed files with 1329 additions and 958 deletions

View File

@ -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]);

View File

@ -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

View File

@ -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
@ -4862,6 +4872,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()
@ -4889,6 +4921,30 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
void *data),
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().

View File

@ -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.

View File

@ -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 -EINVAL;
mutex_lock(&local->mtx);
/* Check if the operating channel is the requested channel */
if (!need_offchan) {
struct ieee80211_chanctx_conf *chanctx_conf;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf) {
need_offchan = params->chan &&
(params->chan !=
chanctx_conf->def.chan);
} else if (!params->chan) {
ret = -EINVAL;
rcu_read_unlock();
goto out_unlock;
} else {
need_offchan = true;
}
rcu_read_unlock();
}
if (need_offchan && !params->offchan) {
ret = -EBUSY;
goto out_unlock;
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
if (!skb) {
ret = -ENOMEM;
goto out_unlock;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
data = skb_put(skb, params->len);
memcpy(data, params->buf, params->len);
/* Update CSA counters */
if (sdata->vif.csa_active &&
(sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
params->n_csa_offsets) {
int i;
struct beacon_data *beacon = NULL;
rcu_read_lock();
if (sdata->vif.type == NL80211_IFTYPE_AP)
beacon = rcu_dereference(sdata->u.ap.beacon);
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
beacon = rcu_dereference(sdata->u.ibss.presp);
else if (ieee80211_vif_is_mesh(&sdata->vif))
beacon = rcu_dereference(sdata->u.mesh.beacon);
if (beacon)
for (i = 0; i < params->n_csa_offsets; i++)
data[params->csa_offsets[i]] =
beacon->csa_current_counter;
rcu_read_unlock();
}
IEEE80211_SKB_CB(skb)->flags = flags;
skb->dev = sdata->dev;
if (!params->dont_wait_for_ack) {
/* make a copy to preserve the frame contents
* in case of encryption.
*/
ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
GFP_KERNEL);
if (IS_ERR(ack_skb)) {
ret = PTR_ERR(ack_skb);
kfree_skb(skb);
goto out_unlock;
}
} else {
/* Assign a dummy non-zero cookie, it's not sent to
* userspace in this case but we rely on its value
* internally in the need_offchan case to distinguish
* mgmt-tx from remain-on-channel.
*/
*cookie = 0xffffffff;
}
if (!need_offchan) {
ieee80211_tx_skb(sdata, skb);
ret = 0;
goto out_unlock;
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
IEEE80211_SKB_CB(skb)->hw_queue =
local->hw.offchannel_tx_hw_queue;
/* This will handle all kinds of coalescing and immediate TX */
ret = ieee80211_start_roc_work(local, sdata, params->chan,
params->wait, cookie, skb,
IEEE80211_ROC_TYPE_MGMT_TX);
if (ret)
kfree_skb(skb);
out_unlock:
mutex_unlock(&local->mtx);
return ret;
}
static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
return ieee80211_cancel_roc(local, cookie, true);
return 0;
}
static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
@ -3569,7 +3091,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc;
struct sk_buff *skb, *ack_skb;
struct sk_buff *skb;
int size = sizeof(*nullfunc);
__le16 fc;
bool qos;
@ -3637,10 +3159,9 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
if (qos)
nullfunc->qos_ctrl = cpu_to_le16(7);
ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
if (IS_ERR(ack_skb)) {
ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC);
if (ret) {
kfree_skb(skb);
ret = PTR_ERR(ack_skb);
goto unlock;
}
@ -3842,6 +3363,7 @@ const struct cfg80211_ops mac80211_config_ops = {
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
.abort_scan = ieee80211_abort_scan,
.sched_scan_start = ieee80211_sched_scan_start,
.sched_scan_stop = ieee80211_sched_scan_stop,
.auth = ieee80211_auth,

View File

@ -125,6 +125,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
FLAG(TDLS_WIDER_BW),
FLAG(SUPPORTS_AMSDU_IN_AMPDU),
FLAG(BEACON_TX_STATUS),
FLAG(NEEDS_UNIQUE_STA_ADDR),
/* keep last for the build bug below */
(void *)0x1

View File

@ -428,6 +428,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
chandef.width = sdata->u.ibss.chandef.width;
break;
case NL80211_CHAN_WIDTH_80:
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
chandef = sdata->u.ibss.chandef;
chandef.chan = cbss->channel;

View File

@ -325,19 +325,15 @@ struct mesh_preq_queue {
struct ieee80211_roc_work {
struct list_head list;
struct list_head dependents;
struct delayed_work work;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
bool started, abort, hw_begun, notified;
bool to_be_freed;
bool on_channel;
unsigned long hw_start_time;
unsigned long start_time;
u32 duration, req_duration;
struct sk_buff *frame;
@ -1335,6 +1331,7 @@ struct ieee80211_local {
/*
* Remain-on-channel support
*/
struct delayed_work roc_work;
struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time;
@ -1483,6 +1480,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
void ieee80211_configure_filter(struct ieee80211_local *local);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local);
int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
u64 *cookie, gfp_t gfp);
/* STA code */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
@ -1577,16 +1578,22 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_local *local);
void ieee80211_sched_scan_end(struct ieee80211_local *local);
void ieee80211_sched_scan_stopped_work(struct work_struct *work);
/* off-channel helpers */
/* off-channel/mgmt-tx */
void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
void ieee80211_offchannel_return(struct ieee80211_local *local);
void ieee80211_roc_setup(struct ieee80211_local *local);
void ieee80211_start_next_roc(struct ieee80211_local *local);
void ieee80211_roc_purge(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
void ieee80211_sw_roc_work(struct work_struct *work);
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan,
unsigned int duration, u64 *cookie);
int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie);
int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params, u64 *cookie);
int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie);
/* channel switch handling */
void ieee80211_csa_finalize_work(struct work_struct *work);

View File

@ -4,6 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -320,7 +321,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
return;
if (new)
list_add_tail(&new->list, &sdata->key_list);
list_add_tail_rcu(&new->list, &sdata->key_list);
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
@ -368,7 +369,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
}
if (old)
list_del(&old->list);
list_del_rcu(&old->list);
}
struct ieee80211_key *
@ -592,8 +593,8 @@ static void ieee80211_key_destroy(struct ieee80211_key *key,
return;
/*
* Synchronize so the TX path can no longer be using
* this key before we free/remove it.
* Synchronize so the TX path and rcu key iterators
* can no longer be using this key before we free/remove it.
*/
synchronize_net();
@ -744,6 +745,53 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_iter_keys);
static void
_ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
struct ieee80211_sub_if_data *sdata,
void (*iter)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key,
void *data),
void *iter_data)
{
struct ieee80211_key *key;
list_for_each_entry_rcu(key, &sdata->key_list, list) {
/* skip keys of station in removal process */
if (key->sta && key->sta->removed)
continue;
if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
continue;
iter(hw, &sdata->vif,
key->sta ? &key->sta->sta : NULL,
&key->conf, iter_data);
}
}
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)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
if (vif) {
sdata = vif_to_sdata(vif);
_ieee80211_iter_keys_rcu(hw, sdata, iter, iter_data);
} else {
list_for_each_entry_rcu(sdata, &local->interfaces, list)
_ieee80211_iter_keys_rcu(hw, sdata, iter, iter_data);
}
}
EXPORT_SYMBOL(ieee80211_iter_keys_rcu);
static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata,
struct list_head *keys)
{

View File

@ -541,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
NL80211_FEATURE_HT_IBSS |
NL80211_FEATURE_VIF_TXPOWER |
NL80211_FEATURE_MAC_ON_CREATE |
NL80211_FEATURE_USERSPACE_MPM;
NL80211_FEATURE_USERSPACE_MPM |
NL80211_FEATURE_FULL_AP_CLIENT_STATE;
if (!ops->hw_scan)
wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
@ -1148,6 +1149,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
rtnl_unlock();
cancel_delayed_work_sync(&local->roc_work);
cancel_work_sync(&local->restart_work);
cancel_work_sync(&local->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);

View File

@ -968,8 +968,8 @@ int mesh_path_send_to_gates(struct mesh_path *mpath)
copy = true;
} else {
mpath_dbg(sdata,
"Not forwarding %p (flags %#x)\n",
gate->mpath, gate->mpath->flags);
"Not forwarding to %pM (flags %#x)\n",
gate->mpath->dst, gate->mpath->flags);
}
}

View File

@ -1930,7 +1930,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
if (sdata->vif.p2p) {
if (sdata->vif.p2p ||
sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
const struct cfg80211_bss_ies *ies;
rcu_read_lock();
@ -3458,7 +3459,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
if (sdata->vif.p2p) {
if (sdata->vif.p2p ||
sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
struct ieee80211_p2p_noa_attr noa = {};
int ret;

View File

@ -187,11 +187,80 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
false);
}
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
{
if (roc->notified)
/* was never transmitted */
if (roc->frame) {
cfg80211_mgmt_tx_status(&roc->sdata->wdev, roc->mgmt_tx_cookie,
roc->frame->data, roc->frame->len,
false, GFP_KERNEL);
ieee80211_free_txskb(&roc->sdata->local->hw, roc->frame);
}
if (!roc->mgmt_tx_cookie)
cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
roc->cookie, roc->chan,
GFP_KERNEL);
list_del(&roc->list);
kfree(roc);
}
static unsigned long ieee80211_end_finished_rocs(struct ieee80211_local *local,
unsigned long now)
{
struct ieee80211_roc_work *roc, *tmp;
long remaining_dur_min = LONG_MAX;
lockdep_assert_held(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
long remaining;
if (!roc->started)
break;
remaining = roc->start_time +
msecs_to_jiffies(roc->duration) -
now;
/* In case of HW ROC, it is possible that the HW finished the
* ROC session before the actual requested time. In such a case
* end the ROC session (disregarding the remaining time).
*/
if (roc->abort || roc->hw_begun || remaining <= 0)
ieee80211_roc_notify_destroy(roc);
else
remaining_dur_min = min(remaining_dur_min, remaining);
}
return remaining_dur_min;
}
static bool ieee80211_recalc_sw_work(struct ieee80211_local *local,
unsigned long now)
{
long dur = ieee80211_end_finished_rocs(local, now);
if (dur == LONG_MAX)
return false;
mod_delayed_work(local->workqueue, &local->roc_work, dur);
return true;
}
static void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc,
unsigned long start_time)
{
struct ieee80211_local *local = roc->sdata->local;
if (WARN_ON(roc->notified))
return;
roc->start_time = start_time;
roc->started = true;
roc->hw_begun = true;
if (roc->mgmt_tx_cookie) {
if (!WARN_ON(!roc->frame)) {
ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7,
@ -205,40 +274,26 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
}
roc->notified = true;
if (!local->ops->remain_on_channel)
ieee80211_recalc_sw_work(local, start_time);
}
static void ieee80211_hw_roc_start(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, hw_roc_start);
struct ieee80211_roc_work *roc, *dep, *tmp;
struct ieee80211_roc_work *roc;
mutex_lock(&local->mtx);
if (list_empty(&local->roc_list))
goto out_unlock;
list_for_each_entry(roc, &local->roc_list, list) {
if (!roc->started)
break;
roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
list);
if (!roc->started)
goto out_unlock;
roc->hw_begun = true;
roc->hw_start_time = local->hw_roc_start_time;
ieee80211_handle_roc_started(roc);
list_for_each_entry_safe(dep, tmp, &roc->dependents, list) {
ieee80211_handle_roc_started(dep);
if (dep->duration > roc->duration) {
u32 dur = dep->duration;
dep->duration = dur - roc->duration;
roc->duration = dur;
list_move(&dep->list, &roc->list);
}
ieee80211_handle_roc_started(roc, local->hw_roc_start_time);
}
out_unlock:
mutex_unlock(&local->mtx);
}
@ -254,34 +309,40 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
void ieee80211_start_next_roc(struct ieee80211_local *local)
static void _ieee80211_start_next_roc(struct ieee80211_local *local)
{
struct ieee80211_roc_work *roc;
struct ieee80211_roc_work *roc, *tmp;
enum ieee80211_roc_type type;
u32 min_dur, max_dur;
lockdep_assert_held(&local->mtx);
if (list_empty(&local->roc_list)) {
ieee80211_run_deferred_scan(local);
if (WARN_ON(list_empty(&local->roc_list)))
return;
}
roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
list);
if (WARN_ON_ONCE(roc->started))
if (WARN_ON(roc->started))
return;
min_dur = roc->duration;
max_dur = roc->duration;
type = roc->type;
list_for_each_entry(tmp, &local->roc_list, list) {
if (tmp == roc)
continue;
if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
break;
max_dur = max(tmp->duration, max_dur);
min_dur = min(tmp->duration, min_dur);
type = max(tmp->type, type);
}
if (local->ops->remain_on_channel) {
int ret, duration = roc->duration;
/* XXX: duplicated, see ieee80211_start_roc_work() */
if (!duration)
duration = 10;
ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
duration, roc->type);
roc->started = true;
int ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
max_dur, type);
if (ret) {
wiphy_warn(local->hw.wiphy,
@ -290,74 +351,24 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
* queue the work struct again to avoid recursion
* when multiple failures occur
*/
ieee80211_remain_on_channel_expired(&local->hw);
list_for_each_entry(tmp, &local->roc_list, list) {
if (tmp->sdata != roc->sdata ||
tmp->chan != roc->chan)
break;
tmp->started = true;
tmp->abort = true;
}
ieee80211_queue_work(&local->hw, &local->hw_roc_done);
return;
}
/* we'll notify about the start once the HW calls back */
list_for_each_entry(tmp, &local->roc_list, list) {
if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
break;
tmp->started = true;
}
} else {
/* delay it a bit */
ieee80211_queue_delayed_work(&local->hw, &roc->work,
round_jiffies_relative(HZ/2));
}
}
void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free)
{
struct ieee80211_roc_work *dep, *tmp;
if (WARN_ON(roc->to_be_freed))
return;
/* was never transmitted */
if (roc->frame) {
cfg80211_mgmt_tx_status(&roc->sdata->wdev,
(unsigned long)roc->frame,
roc->frame->data, roc->frame->len,
false, GFP_KERNEL);
kfree_skb(roc->frame);
}
if (!roc->mgmt_tx_cookie)
cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
roc->cookie, roc->chan,
GFP_KERNEL);
list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
ieee80211_roc_notify_destroy(dep, true);
if (free)
kfree(roc);
else
roc->to_be_freed = true;
}
void ieee80211_sw_roc_work(struct work_struct *work)
{
struct ieee80211_roc_work *roc =
container_of(work, struct ieee80211_roc_work, work.work);
struct ieee80211_sub_if_data *sdata = roc->sdata;
struct ieee80211_local *local = sdata->local;
bool started, on_channel;
mutex_lock(&local->mtx);
if (roc->to_be_freed)
goto out_unlock;
if (roc->abort)
goto finish;
if (WARN_ON(list_empty(&local->roc_list)))
goto out_unlock;
if (WARN_ON(roc != list_first_entry(&local->roc_list,
struct ieee80211_roc_work,
list)))
goto out_unlock;
if (!roc->started) {
struct ieee80211_roc_work *dep;
WARN_ON(local->use_chanctx);
/* If actually operating on the desired channel (with at least
* 20 MHz channel width) don't stop all the operations but still
* treat it as though the ROC operation started properly, so
@ -377,27 +388,72 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_hw_config(local, 0);
}
/* tell userspace or send frame */
ieee80211_handle_roc_started(roc);
list_for_each_entry(dep, &roc->dependents, list)
ieee80211_handle_roc_started(dep);
ieee80211_queue_delayed_work(&local->hw, &local->roc_work,
msecs_to_jiffies(min_dur));
/* if it was pure TX, just finish right away */
if (!roc->duration)
goto finish;
/* tell userspace or send frame(s) */
list_for_each_entry(tmp, &local->roc_list, list) {
if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
break;
roc->started = true;
ieee80211_queue_delayed_work(&local->hw, &roc->work,
msecs_to_jiffies(roc->duration));
tmp->on_channel = roc->on_channel;
ieee80211_handle_roc_started(tmp, jiffies);
}
}
}
void ieee80211_start_next_roc(struct ieee80211_local *local)
{
struct ieee80211_roc_work *roc;
lockdep_assert_held(&local->mtx);
if (list_empty(&local->roc_list)) {
ieee80211_run_deferred_scan(local);
return;
}
roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
list);
if (WARN_ON_ONCE(roc->started))
return;
if (local->ops->remain_on_channel) {
_ieee80211_start_next_roc(local);
} else {
/* finish this ROC */
finish:
list_del(&roc->list);
started = roc->started;
on_channel = roc->on_channel;
ieee80211_roc_notify_destroy(roc, !roc->abort);
/* delay it a bit */
ieee80211_queue_delayed_work(&local->hw, &local->roc_work,
round_jiffies_relative(HZ/2));
}
}
if (started && !on_channel) {
static void __ieee80211_roc_work(struct ieee80211_local *local)
{
struct ieee80211_roc_work *roc;
bool on_channel;
lockdep_assert_held(&local->mtx);
if (WARN_ON(local->ops->remain_on_channel))
return;
roc = list_first_entry_or_null(&local->roc_list,
struct ieee80211_roc_work, list);
if (!roc)
return;
if (!roc->started) {
WARN_ON(local->use_chanctx);
_ieee80211_start_next_roc(local);
} else {
on_channel = roc->on_channel;
if (ieee80211_recalc_sw_work(local, jiffies))
return;
/* careful - roc pointer became invalid during recalc */
if (!on_channel) {
ieee80211_flush_queues(local, NULL, false);
local->tmp_channel = NULL;
@ -407,14 +463,17 @@ void ieee80211_sw_roc_work(struct work_struct *work)
}
ieee80211_recalc_idle(local);
if (started)
ieee80211_start_next_roc(local);
else if (list_empty(&local->roc_list))
ieee80211_run_deferred_scan(local);
ieee80211_start_next_roc(local);
}
}
out_unlock:
static void ieee80211_roc_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, roc_work.work);
mutex_lock(&local->mtx);
__ieee80211_roc_work(local);
mutex_unlock(&local->mtx);
}
@ -422,27 +481,14 @@ static void ieee80211_hw_roc_done(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, hw_roc_done);
struct ieee80211_roc_work *roc;
mutex_lock(&local->mtx);
if (list_empty(&local->roc_list))
goto out_unlock;
roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
list);
if (!roc->started)
goto out_unlock;
list_del(&roc->list);
ieee80211_roc_notify_destroy(roc, true);
ieee80211_end_finished_rocs(local, jiffies);
/* if there's another roc, start it now */
ieee80211_start_next_roc(local);
out_unlock:
mutex_unlock(&local->mtx);
}
@ -456,10 +502,472 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
static bool
ieee80211_coalesce_hw_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;
if (WARN_ON(!cur_roc->started))
return false;
/* if it was scheduled in the hardware, but not started yet,
* we can only combine if the older one had a longer duration
*/
if (!cur_roc->hw_begun && new_roc->duration > cur_roc->duration)
return false;
remaining = cur_roc->start_time +
msecs_to_jiffies(cur_roc->duration) -
now;
/* if it doesn't fit entirely, schedule a new one */
if (new_roc->duration > jiffies_to_msecs(remaining))
return false;
/* add just after the current one so we combine their finish later */
list_add(&new_roc->list, &cur_roc->list);
/* if the existing one has already begun then let this one also
* begin, otherwise they'll both be marked properly by the work
* struct that runs once the driver notifies us of the beginning
*/
if (cur_roc->hw_begun)
ieee80211_handle_roc_started(new_roc, now);
return true;
}
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, combine_started = true;
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;
/*
* 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 no need to queue, handle it immediately */
if (list_empty(&local->roc_list) &&
!local->scanning && !ieee80211_is_radar_required(local)) {
/* if not HW assist, just queue & schedule work */
if (!local->ops->remain_on_channel) {
list_add_tail(&roc->list, &local->roc_list);
ieee80211_queue_delayed_work(&local->hw,
&local->roc_work, 0);
} else {
/* 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;
list_add_tail(&roc->list, &local->roc_list);
}
return 0;
}
/* otherwise handle queueing */
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, add
* just after the new one to combine.
*/
if (!tmp->started) {
list_add(&roc->list, &tmp->list);
queued = true;
break;
}
if (!combine_started)
continue;
if (!local->ops->remain_on_channel) {
/* If there's no hardware remain-on-channel, and
* doing so won't push us over the maximum r-o-c
* we allow, then we can just add the new one to
* the list and mark it as having started now.
* If it would push over the limit, don't try to
* combine with other started ones (that haven't
* been running as long) but potentially sort it
* with others that had the same fate.
*/
unsigned long now = jiffies;
u32 elapsed = jiffies_to_msecs(now - tmp->start_time);
struct wiphy *wiphy = local->hw.wiphy;
u32 max_roc = wiphy->max_remain_on_channel_duration;
if (elapsed + roc->duration > max_roc) {
combine_started = false;
continue;
}
list_add(&roc->list, &tmp->list);
queued = true;
roc->on_channel = tmp->on_channel;
ieee80211_handle_roc_started(roc, now);
break;
}
queued = ieee80211_coalesce_hw_started_roc(local, roc, tmp);
if (queued)
break;
/* if it wasn't queued, perhaps it can be combined with
* another that also couldn't get combined previously,
* but no need to check for already started ones, since
* that can't work.
*/
combine_started = false;
}
if (!queued)
list_add_tail(&roc->list, &local->roc_list);
return 0;
}
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;
if (!cookie)
return -ENOENT;
mutex_lock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
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;
}
if (!found->started) {
ieee80211_roc_notify_destroy(found);
goto out_unlock;
}
if (local->ops->remain_on_channel) {
ret = drv_cancel_remain_on_channel(local);
if (WARN_ON_ONCE(ret)) {
mutex_unlock(&local->mtx);
return ret;
}
/* TODO:
* if multiple items were combined here then we really shouldn't
* cancel them all - we should wait for as much time as needed
* for the longest remaining one, and only then cancel ...
*/
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
if (!roc->started)
break;
if (roc == found)
found = NULL;
ieee80211_roc_notify_destroy(roc);
}
/* that really must not happen - it was started */
WARN_ON(found);
ieee80211_start_next_roc(local);
} else {
/* go through work struct to return to the operating channel */
found->abort = true;
mod_delayed_work(local->workqueue, &local->roc_work, 0);
}
out_unlock:
mutex_unlock(&local->mtx);
return 0;
}
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);
}
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;
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 -EINVAL;
mutex_lock(&local->mtx);
/* Check if the operating channel is the requested channel */
if (!need_offchan) {
struct ieee80211_chanctx_conf *chanctx_conf;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf) {
need_offchan = params->chan &&
(params->chan !=
chanctx_conf->def.chan);
} else if (!params->chan) {
ret = -EINVAL;
rcu_read_unlock();
goto out_unlock;
} else {
need_offchan = true;
}
rcu_read_unlock();
}
if (need_offchan && !params->offchan) {
ret = -EBUSY;
goto out_unlock;
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
if (!skb) {
ret = -ENOMEM;
goto out_unlock;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
data = skb_put(skb, params->len);
memcpy(data, params->buf, params->len);
/* Update CSA counters */
if (sdata->vif.csa_active &&
(sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
params->n_csa_offsets) {
int i;
struct beacon_data *beacon = NULL;
rcu_read_lock();
if (sdata->vif.type == NL80211_IFTYPE_AP)
beacon = rcu_dereference(sdata->u.ap.beacon);
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
beacon = rcu_dereference(sdata->u.ibss.presp);
else if (ieee80211_vif_is_mesh(&sdata->vif))
beacon = rcu_dereference(sdata->u.mesh.beacon);
if (beacon)
for (i = 0; i < params->n_csa_offsets; i++)
data[params->csa_offsets[i]] =
beacon->csa_current_counter;
rcu_read_unlock();
}
IEEE80211_SKB_CB(skb)->flags = flags;
skb->dev = sdata->dev;
if (!params->dont_wait_for_ack) {
/* make a copy to preserve the frame contents
* in case of encryption.
*/
ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_KERNEL);
if (ret) {
kfree_skb(skb);
goto out_unlock;
}
} else {
/* Assign a dummy non-zero cookie, it's not sent to
* userspace in this case but we rely on its value
* internally in the need_offchan case to distinguish
* mgmt-tx from remain-on-channel.
*/
*cookie = 0xffffffff;
}
if (!need_offchan) {
ieee80211_tx_skb(sdata, skb);
ret = 0;
goto out_unlock;
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
IEEE80211_SKB_CB(skb)->hw_queue =
local->hw.offchannel_tx_hw_queue;
/* This will handle all kinds of coalescing and immediate TX */
ret = ieee80211_start_roc_work(local, sdata, params->chan,
params->wait, cookie, skb,
IEEE80211_ROC_TYPE_MGMT_TX);
if (ret)
ieee80211_free_txskb(&local->hw, skb);
out_unlock:
mutex_unlock(&local->mtx);
return ret;
}
int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
return ieee80211_cancel_roc(local, cookie, true);
}
void ieee80211_roc_setup(struct ieee80211_local *local)
{
INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
INIT_DELAYED_WORK(&local->roc_work, ieee80211_roc_work);
INIT_LIST_HEAD(&local->roc_list);
}
@ -467,36 +975,27 @@ void ieee80211_roc_purge(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_roc_work *roc, *tmp;
LIST_HEAD(tmp_list);
bool work_to_do = false;
mutex_lock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
if (sdata && roc->sdata != sdata)
continue;
if (roc->started && local->ops->remain_on_channel) {
/* can race, so ignore return value */
drv_cancel_remain_on_channel(local);
}
list_move_tail(&roc->list, &tmp_list);
roc->abort = true;
}
mutex_unlock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
if (local->ops->remain_on_channel) {
list_del(&roc->list);
ieee80211_roc_notify_destroy(roc, true);
if (roc->started) {
if (local->ops->remain_on_channel) {
/* can race, so ignore return value */
drv_cancel_remain_on_channel(local);
ieee80211_roc_notify_destroy(roc);
} else {
roc->abort = true;
work_to_do = true;
}
} else {
ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
/* work will clean up etc */
flush_delayed_work(&roc->work);
WARN_ON(!roc->to_be_freed);
kfree(roc);
ieee80211_roc_notify_destroy(roc);
}
}
WARN_ON_ONCE(!list_empty(&tmp_list));
if (work_to_do)
__ieee80211_roc_work(local);
mutex_unlock(&local->mtx);
}

View File

@ -661,8 +661,7 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx)
{
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
WARN_ONCE((unsigned long)rx->skb->data & 1,
"unaligned packet at 0x%p\n", rx->skb->data);
WARN_ON_ONCE((unsigned long)rx->skb->data & 1);
#endif
}

View File

@ -2,6 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -435,6 +436,19 @@ static int sta_info_insert_check(struct sta_info *sta)
is_multicast_ether_addr(sta->sta.addr)))
return -EINVAL;
/* Strictly speaking this isn't necessary as we hold the mutex, but
* the rhashtable code can't really deal with that distinction. We
* do require the mutex for correctness though.
*/
rcu_read_lock();
lockdep_assert_held(&sdata->local->sta_mtx);
if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) &&
ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) {
rcu_read_unlock();
return -ENOTUNIQ;
}
rcu_read_unlock();
return 0;
}
@ -554,14 +568,15 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
might_sleep();
mutex_lock(&local->sta_mtx);
err = sta_info_insert_check(sta);
if (err) {
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
goto out_free;
}
mutex_lock(&local->sta_mtx);
err = sta_info_insert_finish(sta);
if (err)
goto out_free;
@ -868,6 +883,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
}
list_del_rcu(&sta->list);
sta->removed = true;
drv_sta_pre_rcu_remove(local, sta->sdata, sta);
@ -1230,11 +1246,11 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
ieee80211_check_fast_xmit(sta);
}
static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, int tid,
static void ieee80211_send_null_response(struct sta_info *sta, int tid,
enum ieee80211_frame_release_type reason,
bool call_driver)
bool call_driver, bool more_data)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc;
struct sk_buff *skb;
@ -1274,9 +1290,13 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
if (qos) {
nullfunc->qos_ctrl = cpu_to_le16(tid);
if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
if (reason == IEEE80211_FRAME_RELEASE_UAPSD) {
nullfunc->qos_ctrl |=
cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
if (more_data)
nullfunc->frame_control |=
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
}
info = IEEE80211_SKB_CB(skb);
@ -1323,22 +1343,48 @@ static int find_highest_prio_tid(unsigned long tids)
return fls(tids) - 1;
}
/* Indicates if the MORE_DATA bit should be set in the last
* frame obtained by ieee80211_sta_ps_get_frames.
* Note that driver_release_tids is relevant only if
* reason = IEEE80211_FRAME_RELEASE_PSPOLL
*/
static bool
ieee80211_sta_ps_more_data(struct sta_info *sta, u8 ignored_acs,
enum ieee80211_frame_release_type reason,
unsigned long driver_release_tids)
{
int ac;
/* If the driver has data on more than one TID then
* certainly there's more data if we release just a
* single frame now (from a single TID). This will
* only happen for PS-Poll.
*/
if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
hweight16(driver_release_tids) > 1)
return true;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
if (ignored_acs & BIT(ac))
continue;
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
!skb_queue_empty(&sta->ps_tx_buf[ac]))
return true;
}
return false;
}
static void
ieee80211_sta_ps_deliver_response(struct sta_info *sta,
int n_frames, u8 ignored_acs,
enum ieee80211_frame_release_type reason)
ieee80211_sta_ps_get_frames(struct sta_info *sta, int n_frames, u8 ignored_acs,
enum ieee80211_frame_release_type reason,
struct sk_buff_head *frames,
unsigned long *driver_release_tids)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
bool more_data = false;
int ac;
unsigned long driver_release_tids = 0;
struct sk_buff_head frames;
/* Service or PS-Poll period starts */
set_sta_flag(sta, WLAN_STA_SP);
__skb_queue_head_init(&frames);
/* Get response frame(s) and more data bit for the last one. */
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
@ -1352,26 +1398,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* if we already have frames from software, then we can't also
* release from hardware queues
*/
if (skb_queue_empty(&frames)) {
driver_release_tids |= sta->driver_buffered_tids & tids;
driver_release_tids |= sta->txq_buffered_tids & tids;
if (skb_queue_empty(frames)) {
*driver_release_tids |=
sta->driver_buffered_tids & tids;
*driver_release_tids |= sta->txq_buffered_tids & tids;
}
if (driver_release_tids) {
/* If the driver has data on more than one TID then
* certainly there's more data if we release just a
* single frame now (from a single TID). This will
* only happen for PS-Poll.
*/
if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
hweight16(driver_release_tids) > 1) {
more_data = true;
driver_release_tids =
BIT(find_highest_prio_tid(
driver_release_tids));
break;
}
} else {
if (!*driver_release_tids) {
struct sk_buff *skb;
while (n_frames > 0) {
@ -1385,20 +1418,44 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
if (!skb)
break;
n_frames--;
__skb_queue_tail(&frames, skb);
__skb_queue_tail(frames, skb);
}
}
/* If we have more frames buffered on this AC, then set the
* more-data bit and abort the loop since we can't send more
* data from other ACs before the buffered frames from this.
/* If we have more frames buffered on this AC, then abort the
* loop since we can't send more data from other ACs before
* the buffered frames from this.
*/
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
!skb_queue_empty(&sta->ps_tx_buf[ac])) {
more_data = true;
!skb_queue_empty(&sta->ps_tx_buf[ac]))
break;
}
}
}
static void
ieee80211_sta_ps_deliver_response(struct sta_info *sta,
int n_frames, u8 ignored_acs,
enum ieee80211_frame_release_type reason)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
unsigned long driver_release_tids = 0;
struct sk_buff_head frames;
bool more_data;
/* Service or PS-Poll period starts */
set_sta_flag(sta, WLAN_STA_SP);
__skb_queue_head_init(&frames);
ieee80211_sta_ps_get_frames(sta, n_frames, ignored_acs, reason,
&frames, &driver_release_tids);
more_data = ieee80211_sta_ps_more_data(sta, ignored_acs, reason, driver_release_tids);
if (reason == IEEE80211_FRAME_RELEASE_PSPOLL)
driver_release_tids =
BIT(find_highest_prio_tid(driver_release_tids));
if (skb_queue_empty(&frames) && !driver_release_tids) {
int tid;
@ -1421,7 +1478,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* This will evaluate to 1, 3, 5 or 7. */
tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
ieee80211_send_null_response(sdata, sta, tid, reason, true);
ieee80211_send_null_response(sta, tid, reason, true, false);
} else if (!driver_release_tids) {
struct sk_buff_head pending;
struct sk_buff *skb;
@ -1521,8 +1578,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
if (need_null)
ieee80211_send_null_response(
sdata, sta, find_highest_prio_tid(tids),
reason, false);
sta, find_highest_prio_tid(tids),
reason, false, false);
sta_info_recalc_tim(sta);
} else {
@ -1660,6 +1717,22 @@ void ieee80211_sta_eosp(struct ieee80211_sta *pubsta)
}
EXPORT_SYMBOL(ieee80211_sta_eosp);
void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid)
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
enum ieee80211_frame_release_type reason;
bool more_data;
trace_api_send_eosp_nullfunc(sta->local, pubsta, tid);
reason = IEEE80211_FRAME_RELEASE_UAPSD;
more_data = ieee80211_sta_ps_more_data(sta, ~sta->sta.uapsd_queues,
reason, 0);
ieee80211_send_null_response(sta, tid, reason, false, more_data);
}
EXPORT_SYMBOL(ieee80211_send_eosp_nullfunc);
void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
u8 tid, bool buffered)
{

View File

@ -367,6 +367,7 @@ DECLARE_EWMA(signal, 1024, 8)
* @mesh: mesh STA information
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @removed: set to true when sta is being removed from sta_list
* @uploaded: set to true when sta is uploaded to the driver
* @sta: station information we share with the driver
* @sta_state: duplicates information about station state (for debug)
@ -412,6 +413,7 @@ struct sta_info {
u16 listen_interval;
bool dead;
bool removed;
bool uploaded;

View File

@ -2027,6 +2027,31 @@ TRACE_EVENT(api_eosp,
)
);
TRACE_EVENT(api_send_eosp_nullfunc,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta,
u8 tid),
TP_ARGS(local, sta, tid),
TP_STRUCT__entry(
LOCAL_ENTRY
STA_ENTRY
__field(u8, tid)
),
TP_fast_assign(
LOCAL_ASSIGN;
STA_ASSIGN;
__entry->tid = tid;
),
TP_printk(
LOCAL_PR_FMT STA_PR_FMT " tid:%d",
LOCAL_PR_ARG, STA_PR_ARG, __entry->tid
)
);
TRACE_EVENT(api_sta_set_buffered,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta,

View File

@ -1431,7 +1431,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
info->hw_queue =
vif->hw_queue[skb_get_queue_mapping(skb)];
} else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
dev_kfree_skb(skb);
ieee80211_purge_tx_queue(&local->hw, skbs);
return true;
} else
vif = NULL;

View File

@ -288,10 +288,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
if (!test_bit(reason, &local->queue_stop_reasons[queue]))
return;
if (!refcounted)
if (!refcounted) {
local->q_stop_reasons[queue][reason] = 0;
else
} else {
local->q_stop_reasons[queue][reason]--;
if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))
local->q_stop_reasons[queue][reason] = 0;
}
if (local->q_stop_reasons[queue][reason] == 0)
__clear_bit(reason, &local->queue_stop_reasons[queue]);

View File

@ -416,13 +416,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
void cfg80211_process_wdev_events(struct wireless_dev *wdev);
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
enum nl80211_iftype iftype,
struct ieee80211_channel *chan,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
/**
* cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
* @wiphy: the wiphy to validate against

View File

@ -311,8 +311,8 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
keyidx >>= 6;
if (key->key_idx != keyidx) {
printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
"keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
net_dbg_ratelimited("CCMP: RX tkey->key_idx=%d frame keyidx=%d\n",
key->key_idx, keyidx);
return -6;
}
if (!key->key_set) {

View File

@ -434,8 +434,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
keyidx >>= 6;
if (tkey->key_idx != keyidx) {
printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
"keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
net_dbg_ratelimited("TKIP: RX tkey->key_idx=%d frame keyidx=%d\n",
tkey->key_idx, keyidx);
return -6;
}
if (!tkey->key_set) {

View File

@ -4256,8 +4256,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
* station. Include these parameters here and will check them in
* cfg80211_check_station_change().
*/
if (info->attrs[NL80211_ATTR_PEER_AID])
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
if (info->attrs[NL80211_ATTR_STA_AID])
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
params.listen_interval =
@ -4359,6 +4359,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
struct station_parameters params;
u8 *mac_addr = NULL;
u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED);
memset(&params, 0, sizeof(params));
@ -4470,11 +4472,24 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* allow authenticated/associated only if driver handles it */
if (!(rdev->wiphy.features &
NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
params.sta_flags_mask &
(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED)))
params.sta_flags_mask & auth_assoc)
return -EINVAL;
/* Older userspace, or userspace wanting to be compatible with
* !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth
* and assoc flags in the mask, but assumes the station will be
* added as associated anyway since this was the required driver
* behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was
* introduced.
* In order to not bother drivers with this quirk in the API
* set the flags in both the mask and set for new stations in
* this case.
*/
if (!(params.sta_flags_mask & auth_assoc)) {
params.sta_flags_mask |= auth_assoc;
params.sta_flags_set |= auth_assoc;
}
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan))
@ -5997,6 +6012,24 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
if (!rdev->ops->abort_scan)
return -EOPNOTSUPP;
if (rdev->scan_msg)
return 0;
if (!rdev->scan_req)
return -ENOENT;
rdev_abort_scan(rdev, wdev);
return 0;
}
static int
nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
struct cfg80211_sched_scan_request *request,
@ -6507,8 +6540,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (WARN_ON(!cac_time_ms))
cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef,
cac_time_ms);
err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
if (!err) {
wdev->chandef = chandef;
wdev->cac_started = true;
@ -7571,7 +7603,7 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
return -EINVAL;
err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
err = rdev_set_mcast_rate(rdev, dev, mcast_rate);
return err;
}
@ -9716,7 +9748,7 @@ static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
cfg80211_rdev_free_coalesce(rdev);
rdev->ops->set_coalesce(&rdev->wiphy, NULL);
rdev_set_coalesce(rdev, NULL);
return 0;
}
@ -9744,7 +9776,7 @@ static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
i++;
}
err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
err = rdev_set_coalesce(rdev, &new_coalesce);
if (err)
goto error;
@ -10945,6 +10977,14 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_ABORT_SCAN,
.doit = nl80211_abort_scan,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_GET_SCAN,
.policy = nl80211_policy,

View File

@ -29,6 +29,9 @@ int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
return -EOPNOTSUPP;
if (!rdev->ops->join_ocb)
return -EOPNOTSUPP;
if (WARN_ON(!setup->chandef.chan))
return -EINVAL;

View File

@ -427,6 +427,14 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
return ret;
}
static inline void rdev_abort_scan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
trace_rdev_abort_scan(&rdev->wiphy, wdev);
rdev->ops->abort_scan(&rdev->wiphy, wdev);
trace_rdev_return_void(&rdev->wiphy);
}
static inline int rdev_auth(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_auth_request *req)
@ -1020,4 +1028,47 @@ rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
trace_rdev_return_void(&rdev->wiphy);
}
static inline int
rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_chan_def *chandef,
u32 cac_time_ms)
{
int ret = -ENOTSUPP;
trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
cac_time_ms);
if (rdev->ops->start_radar_detection)
ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
chandef, cac_time_ms);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_set_mcast_rate(struct cfg80211_registered_device *rdev,
struct net_device *dev,
int mcast_rate[IEEE80211_NUM_BANDS])
{
int ret = -ENOTSUPP;
trace_rdev_set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
if (rdev->ops->set_mcast_rate)
ret = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_set_coalesce(struct cfg80211_registered_device *rdev,
struct cfg80211_coalesce *coalesce)
{
int ret = -ENOTSUPP;
trace_rdev_set_coalesce(&rdev->wiphy, coalesce);
if (rdev->ops->set_coalesce)
ret = rdev->ops->set_coalesce(&rdev->wiphy, coalesce);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
#endif /* __CFG80211_RDEV_OPS */

View File

@ -1052,7 +1052,7 @@ static u32 map_regdom_flags(u32 rd_flags)
}
static const struct ieee80211_reg_rule *
freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
freq_reg_info_regd(u32 center_freq,
const struct ieee80211_regdomain *regd, u32 bw)
{
int i;
@ -1097,7 +1097,7 @@ __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
reg_rule = freq_reg_info_regd(wiphy, center_freq, regd, bw);
reg_rule = freq_reg_info_regd(center_freq, regd, bw);
if (!IS_ERR(reg_rule))
return reg_rule;
}
@ -1166,6 +1166,41 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
#endif
}
static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
const struct ieee80211_reg_rule *reg_rule,
const struct ieee80211_channel *chan)
{
const struct ieee80211_freq_range *freq_range = NULL;
u32 max_bandwidth_khz, bw_flags = 0;
freq_range = &reg_rule->freq_range;
max_bandwidth_khz = freq_range->max_bandwidth_khz;
/* Check if auto calculation requested */
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
return bw_flags;
}
/*
* Note that right now we assume the desired channel bandwidth
* is always 20 MHz for each individual channel (HT40 uses 20 MHz
@ -1178,11 +1213,9 @@ static void handle_channel(struct wiphy *wiphy,
u32 flags, bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
const struct ieee80211_regdomain *regd;
u32 max_bandwidth_khz;
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
@ -1223,31 +1256,7 @@ static void handle_channel(struct wiphy *wiphy,
chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
max_bandwidth_khz = freq_range->max_bandwidth_khz;
/* Check if auto calculation requested */
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
@ -1760,13 +1769,10 @@ static void handle_channel_custom(struct wiphy *wiphy,
u32 bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL;
u32 max_bandwidth_khz;
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
reg_rule = freq_reg_info_regd(wiphy,
MHZ_TO_KHZ(chan->center_freq),
reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq),
regd, bw);
if (!IS_ERR(reg_rule))
break;
@ -1787,31 +1793,7 @@ static void handle_channel_custom(struct wiphy *wiphy,
chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
freq_range = &reg_rule->freq_range;
max_bandwidth_khz = freq_range->max_bandwidth_khz;
/* Check if auto calculation requested */
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
chan->dfs_state_entered = jiffies;
chan->dfs_state = NL80211_DFS_USABLE;

View File

@ -623,12 +623,24 @@ DECLARE_EVENT_CLASS(station_add_change,
__field(u32, sta_flags_set)
__field(u32, sta_modify_mask)
__field(int, listen_interval)
__field(u16, capability)
__field(u16, aid)
__field(u8, plink_action)
__field(u8, plink_state)
__field(u8, uapsd_queues)
__field(u8, max_sp)
__field(u8, opmode_notif)
__field(bool, opmode_notif_used)
__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
__array(u8, vht_capa, (int)sizeof(struct ieee80211_vht_cap))
__array(char, vlan, IFNAMSIZ)
__dynamic_array(u8, supported_rates,
params->supported_rates_len)
__dynamic_array(u8, ext_capab, params->ext_capab_len)
__dynamic_array(u8, supported_channels,
params->supported_channels_len)
__dynamic_array(u8, supported_oper_classes,
params->supported_oper_classes_len)
),
TP_fast_assign(
WIPHY_ASSIGN;
@ -646,9 +658,35 @@ DECLARE_EVENT_CLASS(station_add_change,
if (params->ht_capa)
memcpy(__entry->ht_capa, params->ht_capa,
sizeof(struct ieee80211_ht_cap));
memset(__entry->vht_capa, 0, sizeof(struct ieee80211_vht_cap));
if (params->vht_capa)
memcpy(__entry->vht_capa, params->vht_capa,
sizeof(struct ieee80211_vht_cap));
memset(__entry->vlan, 0, sizeof(__entry->vlan));
if (params->vlan)
memcpy(__entry->vlan, params->vlan->name, IFNAMSIZ);
if (params->supported_rates && params->supported_rates_len)
memcpy(__get_dynamic_array(supported_rates),
params->supported_rates,
params->supported_rates_len);
if (params->ext_capab && params->ext_capab_len)
memcpy(__get_dynamic_array(ext_capab),
params->ext_capab,
params->ext_capab_len);
if (params->supported_channels &&
params->supported_channels_len)
memcpy(__get_dynamic_array(supported_channels),
params->supported_channels,
params->supported_channels_len);
if (params->supported_oper_classes &&
params->supported_oper_classes_len)
memcpy(__get_dynamic_array(supported_oper_classes),
params->supported_oper_classes,
params->supported_oper_classes_len);
__entry->max_sp = params->max_sp;
__entry->capability = params->capability;
__entry->opmode_notif = params->opmode_notif;
__entry->opmode_notif_used = params->opmode_notif_used;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
", station flags mask: %u, station flags set: %u, "
@ -2818,6 +2856,71 @@ TRACE_EVENT(cfg80211_stop_iface,
WIPHY_PR_ARG, WDEV_PR_ARG)
);
TRACE_EVENT(rdev_start_radar_detection,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_chan_def *chandef,
u32 cac_time_ms),
TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
CHAN_DEF_ENTRY
__field(u32, cac_time_ms)
),
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
CHAN_DEF_ASSIGN(chandef);
__entry->cac_time_ms = cac_time_ms;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
", cac_time_ms=%u",
WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
__entry->cac_time_ms)
);
TRACE_EVENT(rdev_set_mcast_rate,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
int mcast_rate[IEEE80211_NUM_BANDS]),
TP_ARGS(wiphy, netdev, mcast_rate),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
__array(int, mcast_rate, IEEE80211_NUM_BANDS)
),
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
memcpy(__entry->mcast_rate, mcast_rate,
sizeof(int) * IEEE80211_NUM_BANDS);
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", "
"mcast_rates [2.4GHz=0x%x, 5.2GHz=0x%x, 60GHz=0x%x]",
WIPHY_PR_ARG, NETDEV_PR_ARG,
__entry->mcast_rate[IEEE80211_BAND_2GHZ],
__entry->mcast_rate[IEEE80211_BAND_5GHZ],
__entry->mcast_rate[IEEE80211_BAND_60GHZ])
);
TRACE_EVENT(rdev_set_coalesce,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_coalesce *coalesce),
TP_ARGS(wiphy, coalesce),
TP_STRUCT__entry(
WIPHY_ENTRY
__field(int, n_rules)
),
TP_fast_assign(
WIPHY_ASSIGN;
__entry->n_rules = coalesce ? coalesce->n_rules : 0;
),
TP_printk(WIPHY_PR_FMT ", n_rules=%d",
WIPHY_PR_ARG, __entry->n_rules)
);
DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
TP_ARGS(wiphy, wdev)
);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH

View File

@ -1325,13 +1325,6 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
}
EXPORT_SYMBOL(ieee80211_ie_split_ric);
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);
}
EXPORT_SYMBOL(ieee80211_ie_split);
bool ieee80211_operating_class_to_band(u8 operating_class,
enum ieee80211_band *band)
{
@ -1620,120 +1613,6 @@ int cfg80211_check_combinations(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_check_combinations);
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
enum nl80211_iftype iftype,
struct ieee80211_channel *chan,
enum cfg80211_chan_mode chanmode,
u8 radar_detect)
{
struct wireless_dev *wdev_iter;
int num[NUM_NL80211_IFTYPES];
struct ieee80211_channel
*used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
struct ieee80211_channel *ch;
enum cfg80211_chan_mode chmode;
int num_different_channels = 0;
int total = 1;
int i;
ASSERT_RTNL();
if (WARN_ON(hweight32(radar_detect) > 1))
return -EINVAL;
if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
return -EINVAL;
/* Always allow software iftypes */
if (rdev->wiphy.software_iftypes & BIT(iftype)) {
if (radar_detect)
return -EINVAL;
return 0;
}
memset(num, 0, sizeof(num));
memset(used_channels, 0, sizeof(used_channels));
num[iftype] = 1;
/* TODO: We'll probably not need this anymore, since this
* should only be called with CHAN_MODE_UNDEFINED. There are
* still a couple of pending calls where other chanmodes are
* used, but we should get rid of them.
*/
switch (chanmode) {
case CHAN_MODE_UNDEFINED:
break;
case CHAN_MODE_SHARED:
WARN_ON(!chan);
used_channels[0] = chan;
num_different_channels++;
break;
case CHAN_MODE_EXCLUSIVE:
num_different_channels++;
break;
}
list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
if (wdev_iter == wdev)
continue;
if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) {
if (!wdev_iter->p2p_started)
continue;
} else if (wdev_iter->netdev) {
if (!netif_running(wdev_iter->netdev))
continue;
} else {
WARN_ON(1);
}
if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
continue;
/*
* We may be holding the "wdev" mutex, but now need to lock
* wdev_iter. This is OK because once we get here wdev_iter
* is not wdev (tested above), but we need to use the nested
* locking for lockdep.
*/
mutex_lock_nested(&wdev_iter->mtx, 1);
__acquire(wdev_iter->mtx);
cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect);
wdev_unlock(wdev_iter);
switch (chmode) {
case CHAN_MODE_UNDEFINED:
break;
case CHAN_MODE_SHARED:
for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
if (!used_channels[i] || used_channels[i] == ch)
break;
if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS)
return -EBUSY;
if (used_channels[i] == NULL) {
used_channels[i] = ch;
num_different_channels++;
}
break;
case CHAN_MODE_EXCLUSIVE:
num_different_channels++;
break;
}
num[wdev_iter->iftype]++;
total++;
}
if (total == 1 && !radar_detect)
return 0;
return cfg80211_check_combinations(&rdev->wiphy, num_different_channels,
radar_detect, num);
}
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
const u8 *rates, unsigned int n_rates,
u32 *mask)