aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/cfg.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c142
1 files changed, 142 insertions, 0 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 5892b0302454..4bc8a9250cfd 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1593,6 +1593,37 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
1593 return 0; 1593 return 0;
1594} 1594}
1595 1595
1596static int ieee80211_remain_on_channel_hw(struct ieee80211_local *local,
1597 struct net_device *dev,
1598 struct ieee80211_channel *chan,
1599 enum nl80211_channel_type chantype,
1600 unsigned int duration, u64 *cookie)
1601{
1602 int ret;
1603 u32 random_cookie;
1604
1605 lockdep_assert_held(&local->mtx);
1606
1607 if (local->hw_roc_cookie)
1608 return -EBUSY;
1609 /* must be nonzero */
1610 random_cookie = random32() | 1;
1611
1612 *cookie = random_cookie;
1613 local->hw_roc_dev = dev;
1614 local->hw_roc_cookie = random_cookie;
1615 local->hw_roc_channel = chan;
1616 local->hw_roc_channel_type = chantype;
1617 local->hw_roc_duration = duration;
1618 ret = drv_remain_on_channel(local, chan, chantype, duration);
1619 if (ret) {
1620 local->hw_roc_channel = NULL;
1621 local->hw_roc_cookie = 0;
1622 }
1623
1624 return ret;
1625}
1626
1596static int ieee80211_remain_on_channel(struct wiphy *wiphy, 1627static int ieee80211_remain_on_channel(struct wiphy *wiphy,
1597 struct net_device *dev, 1628 struct net_device *dev,
1598 struct ieee80211_channel *chan, 1629 struct ieee80211_channel *chan,
@@ -1601,16 +1632,63 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
1601 u64 *cookie) 1632 u64 *cookie)
1602{ 1633{
1603 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 1634 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1635 struct ieee80211_local *local = sdata->local;
1636
1637 if (local->ops->remain_on_channel) {
1638 int ret;
1639
1640 mutex_lock(&local->mtx);
1641 ret = ieee80211_remain_on_channel_hw(local, dev,
1642 chan, channel_type,
1643 duration, cookie);
1644 local->hw_roc_for_tx = false;
1645 mutex_unlock(&local->mtx);
1646
1647 return ret;
1648 }
1604 1649
1605 return ieee80211_wk_remain_on_channel(sdata, chan, channel_type, 1650 return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
1606 duration, cookie); 1651 duration, cookie);
1607} 1652}
1608 1653
1654static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local,
1655 u64 cookie)
1656{
1657 int ret;
1658
1659 lockdep_assert_held(&local->mtx);
1660
1661 if (local->hw_roc_cookie != cookie)
1662 return -ENOENT;
1663
1664 ret = drv_cancel_remain_on_channel(local);
1665 if (ret)
1666 return ret;
1667
1668 local->hw_roc_cookie = 0;
1669 local->hw_roc_channel = NULL;
1670
1671 ieee80211_recalc_idle(local);
1672
1673 return 0;
1674}
1675
1609static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, 1676static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
1610 struct net_device *dev, 1677 struct net_device *dev,
1611 u64 cookie) 1678 u64 cookie)
1612{ 1679{
1613 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 1680 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1681 struct ieee80211_local *local = sdata->local;
1682
1683 if (local->ops->cancel_remain_on_channel) {
1684 int ret;
1685
1686 mutex_lock(&local->mtx);
1687 ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
1688 mutex_unlock(&local->mtx);
1689
1690 return ret;
1691 }
1614 1692
1615 return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); 1693 return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
1616} 1694}
@@ -1662,6 +1740,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1662 channel_type != local->_oper_channel_type)) 1740 channel_type != local->_oper_channel_type))
1663 is_offchan = true; 1741 is_offchan = true;
1664 1742
1743 if (chan == local->hw_roc_channel) {
1744 /* TODO: check channel type? */
1745 is_offchan = false;
1746 flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
1747 }
1748
1665 if (is_offchan && !offchan) 1749 if (is_offchan && !offchan)
1666 return -EBUSY; 1750 return -EBUSY;
1667 1751
@@ -1700,6 +1784,49 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1700 1784
1701 *cookie = (unsigned long) skb; 1785 *cookie = (unsigned long) skb;
1702 1786
1787 if (is_offchan && local->ops->remain_on_channel) {
1788 unsigned int duration;
1789 int ret;
1790
1791 mutex_lock(&local->mtx);
1792 /*
1793 * If the duration is zero, then the driver
1794 * wouldn't actually do anything. Set it to
1795 * 100 for now.
1796 *
1797 * TODO: cancel the off-channel operation
1798 * when we get the SKB's TX status and
1799 * the wait time was zero before.
1800 */
1801 duration = 100;
1802 if (wait)
1803 duration = wait;
1804 ret = ieee80211_remain_on_channel_hw(local, dev, chan,
1805 channel_type,
1806 duration, cookie);
1807 if (ret) {
1808 kfree_skb(skb);
1809 mutex_unlock(&local->mtx);
1810 return ret;
1811 }
1812
1813 local->hw_roc_for_tx = true;
1814 local->hw_roc_duration = wait;
1815
1816 /*
1817 * queue up frame for transmission after
1818 * ieee80211_ready_on_channel call
1819 */
1820
1821 /* modify cookie to prevent API mismatches */
1822 *cookie ^= 2;
1823 IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
1824 local->hw_roc_skb = skb;
1825 mutex_unlock(&local->mtx);
1826
1827 return 0;
1828 }
1829
1703 /* 1830 /*
1704 * Can transmit right away if the channel was the 1831 * Can transmit right away if the channel was the
1705 * right one and there's no wait involved... If a 1832 * right one and there's no wait involved... If a
@@ -1740,6 +1867,21 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
1740 int ret = -ENOENT; 1867 int ret = -ENOENT;
1741 1868
1742 mutex_lock(&local->mtx); 1869 mutex_lock(&local->mtx);
1870
1871 if (local->ops->cancel_remain_on_channel) {
1872 cookie ^= 2;
1873 ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
1874
1875 if (ret == 0) {
1876 kfree_skb(local->hw_roc_skb);
1877 local->hw_roc_skb = NULL;
1878 }
1879
1880 mutex_unlock(&local->mtx);
1881
1882 return ret;
1883 }
1884
1743 list_for_each_entry(wk, &local->work_list, list) { 1885 list_for_each_entry(wk, &local->work_list, list) {
1744 if (wk->sdata != sdata) 1886 if (wk->sdata != sdata)
1745 continue; 1887 continue;