diff options
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r-- | net/mac80211/cfg.c | 142 |
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 | ||
1596 | static 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 | |||
1596 | static int ieee80211_remain_on_channel(struct wiphy *wiphy, | 1627 | static 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 | ||
1654 | static 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 | |||
1609 | static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, | 1676 | static 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; |