aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-25 04:02:30 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-29 15:24:35 -0500
commitf30221e4ec62d905b56d5e8f7ccab6b406a97cf5 (patch)
tree07d3a4cf6da15a3622dd79c1d72e0e5c9201566a
parentf7ca38dfe58c20cb1aa2ed9643187e8b194b5bae (diff)
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>
-rw-r--r--net/mac80211/cfg.c93
-rw-r--r--net/mac80211/ieee80211_i.h5
-rw-r--r--net/mac80211/status.c15
-rw-r--r--net/mac80211/work.c22
4 files changed, 129 insertions, 6 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index aac2d7de828e..db134b500caa 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1551,6 +1551,28 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
1551 return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); 1551 return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
1552} 1552}
1553 1553
1554static enum work_done_result
1555ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb)
1556{
1557 /*
1558 * Use the data embedded in the work struct for reporting
1559 * here so if the driver mangled the SKB before dropping
1560 * it (which is the only way we really should get here)
1561 * then we don't report mangled data.
1562 *
1563 * If there was no wait time, then by the time we get here
1564 * the driver will likely not have reported the status yet,
1565 * so in that case userspace will have to deal with it.
1566 */
1567
1568 if (wk->offchan_tx.wait && wk->offchan_tx.frame)
1569 cfg80211_mgmt_tx_status(wk->sdata->dev,
1570 (unsigned long) wk->offchan_tx.frame,
1571 wk->ie, wk->ie_len, false, GFP_KERNEL);
1572
1573 return WORK_DONE_DESTROY;
1574}
1575
1554static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, 1576static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1555 struct ieee80211_channel *chan, bool offchan, 1577 struct ieee80211_channel *chan, bool offchan,
1556 enum nl80211_channel_type channel_type, 1578 enum nl80211_channel_type channel_type,
@@ -1561,20 +1583,22 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1561 struct ieee80211_local *local = sdata->local; 1583 struct ieee80211_local *local = sdata->local;
1562 struct sk_buff *skb; 1584 struct sk_buff *skb;
1563 struct sta_info *sta; 1585 struct sta_info *sta;
1586 struct ieee80211_work *wk;
1564 const struct ieee80211_mgmt *mgmt = (void *)buf; 1587 const struct ieee80211_mgmt *mgmt = (void *)buf;
1565 u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | 1588 u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
1566 IEEE80211_TX_CTL_REQ_TX_STATUS; 1589 IEEE80211_TX_CTL_REQ_TX_STATUS;
1567 1590 bool is_offchan = false;
1568 if (offchan)
1569 return -EOPNOTSUPP;
1570 1591
1571 /* Check that we are on the requested channel for transmission */ 1592 /* Check that we are on the requested channel for transmission */
1572 if (chan != local->tmp_channel && 1593 if (chan != local->tmp_channel &&
1573 chan != local->oper_channel) 1594 chan != local->oper_channel)
1574 return -EBUSY; 1595 is_offchan = true;
1575 if (channel_type_valid && 1596 if (channel_type_valid &&
1576 (channel_type != local->tmp_channel_type && 1597 (channel_type != local->tmp_channel_type &&
1577 channel_type != local->_oper_channel_type)) 1598 channel_type != local->_oper_channel_type))
1599 is_offchan = true;
1600
1601 if (is_offchan && !offchan)
1578 return -EBUSY; 1602 return -EBUSY;
1579 1603
1580 switch (sdata->vif.type) { 1604 switch (sdata->vif.type) {
@@ -1608,12 +1632,70 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1608 IEEE80211_SKB_CB(skb)->flags = flags; 1632 IEEE80211_SKB_CB(skb)->flags = flags;
1609 1633
1610 skb->dev = sdata->dev; 1634 skb->dev = sdata->dev;
1611 ieee80211_tx_skb(sdata, skb);
1612 1635
1613 *cookie = (unsigned long) skb; 1636 *cookie = (unsigned long) skb;
1637
1638 /*
1639 * Can transmit right away if the channel was the
1640 * right one and there's no wait involved... If a
1641 * wait is involved, we might otherwise not be on
1642 * the right channel for long enough!
1643 */
1644 if (!is_offchan && !wait && !sdata->vif.bss_conf.idle) {
1645 ieee80211_tx_skb(sdata, skb);
1646 return 0;
1647 }
1648
1649 wk = kzalloc(sizeof(*wk) + len, GFP_KERNEL);
1650 if (!wk) {
1651 kfree_skb(skb);
1652 return -ENOMEM;
1653 }
1654
1655 wk->type = IEEE80211_WORK_OFFCHANNEL_TX;
1656 wk->chan = chan;
1657 wk->sdata = sdata;
1658 wk->done = ieee80211_offchan_tx_done;
1659 wk->offchan_tx.frame = skb;
1660 wk->offchan_tx.wait = wait;
1661 wk->ie_len = len;
1662 memcpy(wk->ie, buf, len);
1663
1664 ieee80211_add_work(wk);
1614 return 0; 1665 return 0;
1615} 1666}
1616 1667
1668static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
1669 struct net_device *dev,
1670 u64 cookie)
1671{
1672 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1673 struct ieee80211_local *local = sdata->local;
1674 struct ieee80211_work *wk;
1675 int ret = -ENOENT;
1676
1677 mutex_lock(&local->mtx);
1678 list_for_each_entry(wk, &local->work_list, list) {
1679 if (wk->sdata != sdata)
1680 continue;
1681
1682 if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
1683 continue;
1684
1685 if (cookie != (unsigned long) wk->offchan_tx.frame)
1686 continue;
1687
1688 wk->timeout = jiffies;
1689
1690 ieee80211_queue_work(&local->hw, &local->work_work);
1691 ret = 0;
1692 break;
1693 }
1694 mutex_unlock(&local->mtx);
1695
1696 return ret;
1697}
1698
1617static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, 1699static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
1618 struct net_device *dev, 1700 struct net_device *dev,
1619 u16 frame_type, bool reg) 1701 u16 frame_type, bool reg)
@@ -1698,6 +1780,7 @@ struct cfg80211_ops mac80211_config_ops = {
1698 .remain_on_channel = ieee80211_remain_on_channel, 1780 .remain_on_channel = ieee80211_remain_on_channel,
1699 .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, 1781 .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
1700 .mgmt_tx = ieee80211_mgmt_tx, 1782 .mgmt_tx = ieee80211_mgmt_tx,
1783 .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
1701 .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, 1784 .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
1702 .mgmt_frame_register = ieee80211_mgmt_frame_register, 1785 .mgmt_frame_register = ieee80211_mgmt_frame_register,
1703 .set_antenna = ieee80211_set_antenna, 1786 .set_antenna = ieee80211_set_antenna,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5bc0745368fe..66b0b52b828d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -260,6 +260,7 @@ enum ieee80211_work_type {
260 IEEE80211_WORK_ASSOC_BEACON_WAIT, 260 IEEE80211_WORK_ASSOC_BEACON_WAIT,
261 IEEE80211_WORK_ASSOC, 261 IEEE80211_WORK_ASSOC,
262 IEEE80211_WORK_REMAIN_ON_CHANNEL, 262 IEEE80211_WORK_REMAIN_ON_CHANNEL,
263 IEEE80211_WORK_OFFCHANNEL_TX,
263}; 264};
264 265
265/** 266/**
@@ -320,6 +321,10 @@ struct ieee80211_work {
320 struct { 321 struct {
321 u32 duration; 322 u32 duration;
322 } remain; 323 } remain;
324 struct {
325 struct sk_buff *frame;
326 u32 wait;
327 } offchan_tx;
323 }; 328 };
324 329
325 int ie_len; 330 int ie_len;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index bed7e32ed908..4958710a7d92 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -321,10 +321,23 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
321 msecs_to_jiffies(10)); 321 msecs_to_jiffies(10));
322 } 322 }
323 323
324 if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) 324 if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
325 struct ieee80211_work *wk;
326
327 rcu_read_lock();
328 list_for_each_entry_rcu(wk, &local->work_list, list) {
329 if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
330 continue;
331 if (wk->offchan_tx.frame != skb)
332 continue;
333 wk->offchan_tx.frame = NULL;
334 break;
335 }
336 rcu_read_unlock();
325 cfg80211_mgmt_tx_status( 337 cfg80211_mgmt_tx_status(
326 skb->dev, (unsigned long) skb, skb->data, skb->len, 338 skb->dev, (unsigned long) skb, skb->data, skb->len,
327 !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); 339 !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
340 }
328 341
329 /* this was a transmitted frame, but now we want to reuse it */ 342 /* this was a transmitted frame, but now we want to reuse it */
330 skb_orphan(skb); 343 skb_orphan(skb);
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index ae344d1ba056..2b5c3f267198 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -561,6 +561,25 @@ ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
561} 561}
562 562
563static enum work_action __must_check 563static enum work_action __must_check
564ieee80211_offchannel_tx(struct ieee80211_work *wk)
565{
566 if (!wk->started) {
567 wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait);
568
569 /*
570 * After this, offchan_tx.frame remains but now is no
571 * longer a valid pointer -- we still need it as the
572 * cookie for canceling this work.
573 */
574 ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame);
575
576 return WORK_ACT_NONE;
577 }
578
579 return WORK_ACT_TIMEOUT;
580}
581
582static enum work_action __must_check
564ieee80211_assoc_beacon_wait(struct ieee80211_work *wk) 583ieee80211_assoc_beacon_wait(struct ieee80211_work *wk)
565{ 584{
566 if (wk->started) 585 if (wk->started)
@@ -955,6 +974,9 @@ static void ieee80211_work_work(struct work_struct *work)
955 case IEEE80211_WORK_REMAIN_ON_CHANNEL: 974 case IEEE80211_WORK_REMAIN_ON_CHANNEL:
956 rma = ieee80211_remain_on_channel_timeout(wk); 975 rma = ieee80211_remain_on_channel_timeout(wk);
957 break; 976 break;
977 case IEEE80211_WORK_OFFCHANNEL_TX:
978 rma = ieee80211_offchannel_tx(wk);
979 break;
958 case IEEE80211_WORK_ASSOC_BEACON_WAIT: 980 case IEEE80211_WORK_ASSOC_BEACON_WAIT:
959 rma = ieee80211_assoc_beacon_wait(wk); 981 rma = ieee80211_assoc_beacon_wait(wk);
960 break; 982 break;