mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-02 18:33:55 +08:00
mac80211: implement off-channel mgmt TX
This implements the new off-channel TX API in mac80211 with a new work item type. The operation doesn't add a new work item when we're on the right channel and there's no wait time so that for example p2p probe responses will be transmitted without delay. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
f7ca38dfe5
commit
f30221e4ec
@ -1551,6 +1551,28 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
|||||||
return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
|
return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum work_done_result
|
||||||
|
ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Use the data embedded in the work struct for reporting
|
||||||
|
* here so if the driver mangled the SKB before dropping
|
||||||
|
* it (which is the only way we really should get here)
|
||||||
|
* then we don't report mangled data.
|
||||||
|
*
|
||||||
|
* If there was no wait time, then by the time we get here
|
||||||
|
* the driver will likely not have reported the status yet,
|
||||||
|
* so in that case userspace will have to deal with it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (wk->offchan_tx.wait && wk->offchan_tx.frame)
|
||||||
|
cfg80211_mgmt_tx_status(wk->sdata->dev,
|
||||||
|
(unsigned long) wk->offchan_tx.frame,
|
||||||
|
wk->ie, wk->ie_len, false, GFP_KERNEL);
|
||||||
|
|
||||||
|
return WORK_DONE_DESTROY;
|
||||||
|
}
|
||||||
|
|
||||||
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
||||||
struct ieee80211_channel *chan, bool offchan,
|
struct ieee80211_channel *chan, bool offchan,
|
||||||
enum nl80211_channel_type channel_type,
|
enum nl80211_channel_type channel_type,
|
||||||
@ -1561,20 +1583,22 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
|||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct sta_info *sta;
|
struct sta_info *sta;
|
||||||
|
struct ieee80211_work *wk;
|
||||||
const struct ieee80211_mgmt *mgmt = (void *)buf;
|
const struct ieee80211_mgmt *mgmt = (void *)buf;
|
||||||
u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
|
u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
|
||||||
IEEE80211_TX_CTL_REQ_TX_STATUS;
|
IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||||
|
bool is_offchan = false;
|
||||||
if (offchan)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
/* Check that we are on the requested channel for transmission */
|
/* Check that we are on the requested channel for transmission */
|
||||||
if (chan != local->tmp_channel &&
|
if (chan != local->tmp_channel &&
|
||||||
chan != local->oper_channel)
|
chan != local->oper_channel)
|
||||||
return -EBUSY;
|
is_offchan = true;
|
||||||
if (channel_type_valid &&
|
if (channel_type_valid &&
|
||||||
(channel_type != local->tmp_channel_type &&
|
(channel_type != local->tmp_channel_type &&
|
||||||
channel_type != local->_oper_channel_type))
|
channel_type != local->_oper_channel_type))
|
||||||
|
is_offchan = true;
|
||||||
|
|
||||||
|
if (is_offchan && !offchan)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
switch (sdata->vif.type) {
|
switch (sdata->vif.type) {
|
||||||
@ -1608,12 +1632,70 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
|
|||||||
IEEE80211_SKB_CB(skb)->flags = flags;
|
IEEE80211_SKB_CB(skb)->flags = flags;
|
||||||
|
|
||||||
skb->dev = sdata->dev;
|
skb->dev = sdata->dev;
|
||||||
ieee80211_tx_skb(sdata, skb);
|
|
||||||
|
|
||||||
*cookie = (unsigned long) skb;
|
*cookie = (unsigned long) skb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can transmit right away if the channel was the
|
||||||
|
* right one and there's no wait involved... If a
|
||||||
|
* wait is involved, we might otherwise not be on
|
||||||
|
* the right channel for long enough!
|
||||||
|
*/
|
||||||
|
if (!is_offchan && !wait && !sdata->vif.bss_conf.idle) {
|
||||||
|
ieee80211_tx_skb(sdata, skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wk = kzalloc(sizeof(*wk) + len, GFP_KERNEL);
|
||||||
|
if (!wk) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
wk->type = IEEE80211_WORK_OFFCHANNEL_TX;
|
||||||
|
wk->chan = chan;
|
||||||
|
wk->sdata = sdata;
|
||||||
|
wk->done = ieee80211_offchan_tx_done;
|
||||||
|
wk->offchan_tx.frame = skb;
|
||||||
|
wk->offchan_tx.wait = wait;
|
||||||
|
wk->ie_len = len;
|
||||||
|
memcpy(wk->ie, buf, len);
|
||||||
|
|
||||||
|
ieee80211_add_work(wk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
|
||||||
|
struct net_device *dev,
|
||||||
|
u64 cookie)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_work *wk;
|
||||||
|
int ret = -ENOENT;
|
||||||
|
|
||||||
|
mutex_lock(&local->mtx);
|
||||||
|
list_for_each_entry(wk, &local->work_list, list) {
|
||||||
|
if (wk->sdata != sdata)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cookie != (unsigned long) wk->offchan_tx.frame)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
wk->timeout = jiffies;
|
||||||
|
|
||||||
|
ieee80211_queue_work(&local->hw, &local->work_work);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mutex_unlock(&local->mtx);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
|
static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
u16 frame_type, bool reg)
|
u16 frame_type, bool reg)
|
||||||
@ -1698,6 +1780,7 @@ struct cfg80211_ops mac80211_config_ops = {
|
|||||||
.remain_on_channel = ieee80211_remain_on_channel,
|
.remain_on_channel = ieee80211_remain_on_channel,
|
||||||
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
|
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
|
||||||
.mgmt_tx = ieee80211_mgmt_tx,
|
.mgmt_tx = ieee80211_mgmt_tx,
|
||||||
|
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
|
||||||
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
|
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
|
||||||
.mgmt_frame_register = ieee80211_mgmt_frame_register,
|
.mgmt_frame_register = ieee80211_mgmt_frame_register,
|
||||||
.set_antenna = ieee80211_set_antenna,
|
.set_antenna = ieee80211_set_antenna,
|
||||||
|
@ -260,6 +260,7 @@ enum ieee80211_work_type {
|
|||||||
IEEE80211_WORK_ASSOC_BEACON_WAIT,
|
IEEE80211_WORK_ASSOC_BEACON_WAIT,
|
||||||
IEEE80211_WORK_ASSOC,
|
IEEE80211_WORK_ASSOC,
|
||||||
IEEE80211_WORK_REMAIN_ON_CHANNEL,
|
IEEE80211_WORK_REMAIN_ON_CHANNEL,
|
||||||
|
IEEE80211_WORK_OFFCHANNEL_TX,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -320,6 +321,10 @@ struct ieee80211_work {
|
|||||||
struct {
|
struct {
|
||||||
u32 duration;
|
u32 duration;
|
||||||
} remain;
|
} remain;
|
||||||
|
struct {
|
||||||
|
struct sk_buff *frame;
|
||||||
|
u32 wait;
|
||||||
|
} offchan_tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
int ie_len;
|
int ie_len;
|
||||||
|
@ -321,10 +321,23 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||||||
msecs_to_jiffies(10));
|
msecs_to_jiffies(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX)
|
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
||||||
|
struct ieee80211_work *wk;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(wk, &local->work_list, list) {
|
||||||
|
if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
|
||||||
|
continue;
|
||||||
|
if (wk->offchan_tx.frame != skb)
|
||||||
|
continue;
|
||||||
|
wk->offchan_tx.frame = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
cfg80211_mgmt_tx_status(
|
cfg80211_mgmt_tx_status(
|
||||||
skb->dev, (unsigned long) skb, skb->data, skb->len,
|
skb->dev, (unsigned long) skb, skb->data, skb->len,
|
||||||
!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
|
!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
|
||||||
|
}
|
||||||
|
|
||||||
/* this was a transmitted frame, but now we want to reuse it */
|
/* this was a transmitted frame, but now we want to reuse it */
|
||||||
skb_orphan(skb);
|
skb_orphan(skb);
|
||||||
|
@ -560,6 +560,25 @@ ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
|
|||||||
return WORK_ACT_TIMEOUT;
|
return WORK_ACT_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum work_action __must_check
|
||||||
|
ieee80211_offchannel_tx(struct ieee80211_work *wk)
|
||||||
|
{
|
||||||
|
if (!wk->started) {
|
||||||
|
wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After this, offchan_tx.frame remains but now is no
|
||||||
|
* longer a valid pointer -- we still need it as the
|
||||||
|
* cookie for canceling this work.
|
||||||
|
*/
|
||||||
|
ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame);
|
||||||
|
|
||||||
|
return WORK_ACT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WORK_ACT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
static enum work_action __must_check
|
static enum work_action __must_check
|
||||||
ieee80211_assoc_beacon_wait(struct ieee80211_work *wk)
|
ieee80211_assoc_beacon_wait(struct ieee80211_work *wk)
|
||||||
{
|
{
|
||||||
@ -955,6 +974,9 @@ static void ieee80211_work_work(struct work_struct *work)
|
|||||||
case IEEE80211_WORK_REMAIN_ON_CHANNEL:
|
case IEEE80211_WORK_REMAIN_ON_CHANNEL:
|
||||||
rma = ieee80211_remain_on_channel_timeout(wk);
|
rma = ieee80211_remain_on_channel_timeout(wk);
|
||||||
break;
|
break;
|
||||||
|
case IEEE80211_WORK_OFFCHANNEL_TX:
|
||||||
|
rma = ieee80211_offchannel_tx(wk);
|
||||||
|
break;
|
||||||
case IEEE80211_WORK_ASSOC_BEACON_WAIT:
|
case IEEE80211_WORK_ASSOC_BEACON_WAIT:
|
||||||
rma = ieee80211_assoc_beacon_wait(wk);
|
rma = ieee80211_assoc_beacon_wait(wk);
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user