Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 224850
b: refs/heads/master
c: f30221e
h: refs/heads/master
v: v3
  • Loading branch information
Johannes Berg authored and John W. Linville committed Nov 29, 2010
1 parent 68447ae commit 2cdce5d
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 7 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f7ca38dfe58c20cb1aa2ed9643187e8b194b5bae
refs/heads/master: f30221e4ec62d905b56d5e8f7ccab6b406a97cf5
93 changes: 88 additions & 5 deletions trunk/net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,28 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
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,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
Expand All @@ -1561,20 +1583,22 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct sta_info *sta;
struct ieee80211_work *wk;
const struct ieee80211_mgmt *mgmt = (void *)buf;
u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
IEEE80211_TX_CTL_REQ_TX_STATUS;

if (offchan)
return -EOPNOTSUPP;
bool is_offchan = false;

/* Check that we are on the requested channel for transmission */
if (chan != local->tmp_channel &&
chan != local->oper_channel)
return -EBUSY;
is_offchan = true;
if (channel_type_valid &&
(channel_type != local->tmp_channel_type &&
channel_type != local->_oper_channel_type))
is_offchan = true;

if (is_offchan && !offchan)
return -EBUSY;

switch (sdata->vif.type) {
Expand Down Expand Up @@ -1608,12 +1632,70 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
IEEE80211_SKB_CB(skb)->flags = flags;

skb->dev = sdata->dev;
ieee80211_tx_skb(sdata, 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;
}

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,
struct net_device *dev,
u16 frame_type, bool reg)
Expand Down Expand Up @@ -1698,6 +1780,7 @@ struct cfg80211_ops mac80211_config_ops = {
.remain_on_channel = ieee80211_remain_on_channel,
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
.mgmt_tx = ieee80211_mgmt_tx,
.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
.mgmt_frame_register = ieee80211_mgmt_frame_register,
.set_antenna = ieee80211_set_antenna,
Expand Down
5 changes: 5 additions & 0 deletions trunk/net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ enum ieee80211_work_type {
IEEE80211_WORK_ASSOC_BEACON_WAIT,
IEEE80211_WORK_ASSOC,
IEEE80211_WORK_REMAIN_ON_CHANNEL,
IEEE80211_WORK_OFFCHANNEL_TX,
};

/**
Expand Down Expand Up @@ -320,6 +321,10 @@ struct ieee80211_work {
struct {
u32 duration;
} remain;
struct {
struct sk_buff *frame;
u32 wait;
} offchan_tx;
};

int ie_len;
Expand Down
15 changes: 14 additions & 1 deletion trunk/net/mac80211/status.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,23 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
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(
skb->dev, (unsigned long) skb, skb->data, skb->len,
!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
}

/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
Expand Down
22 changes: 22 additions & 0 deletions trunk/net/mac80211/work.c
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,25 @@ ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
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
ieee80211_assoc_beacon_wait(struct ieee80211_work *wk)
{
Expand Down Expand Up @@ -955,6 +974,9 @@ static void ieee80211_work_work(struct work_struct *work)
case IEEE80211_WORK_REMAIN_ON_CHANNEL:
rma = ieee80211_remain_on_channel_timeout(wk);
break;
case IEEE80211_WORK_OFFCHANNEL_TX:
rma = ieee80211_offchannel_tx(wk);
break;
case IEEE80211_WORK_ASSOC_BEACON_WAIT:
rma = ieee80211_assoc_beacon_wait(wk);
break;
Expand Down

0 comments on commit 2cdce5d

Please sign in to comment.