diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-11-25 04:02:30 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-11-29 15:24:35 -0500 |
commit | f30221e4ec62d905b56d5e8f7ccab6b406a97cf5 (patch) | |
tree | 07d3a4cf6da15a3622dd79c1d72e0e5c9201566a /net/mac80211/cfg.c | |
parent | f7ca38dfe58c20cb1aa2ed9643187e8b194b5bae (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>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r-- | net/mac80211/cfg.c | 93 |
1 files changed, 88 insertions, 5 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 | ||
1554 | static enum work_done_result | ||
1555 | ieee80211_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 | |||
1554 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | 1576 | static 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 | ||
1668 | static 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 | |||
1617 | static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, | 1699 | static 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, |