aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-12-18 11:20:48 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-01-05 16:07:12 -0500
commit90fc4b3a5ba24f09af2a8c4a723651a328949460 (patch)
tree0ec7031f451f8a7eb38259b6fe80ec52cf780b43 /net
parent21f83589644bb2ed98079bf1e2154c8e70ca6a6c (diff)
mac80211: implement off-channel TX using hw r-o-c offload
When the driver has remain-on-channel offload, implement off-channel transmission using that primitive. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c59
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/offchannel.c30
3 files changed, 81 insertions, 10 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 168a6ba8fc2..4bc8a9250cf 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1641,6 +1641,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
1641 ret = ieee80211_remain_on_channel_hw(local, dev, 1641 ret = ieee80211_remain_on_channel_hw(local, dev,
1642 chan, channel_type, 1642 chan, channel_type,
1643 duration, cookie); 1643 duration, cookie);
1644 local->hw_roc_for_tx = false;
1644 mutex_unlock(&local->mtx); 1645 mutex_unlock(&local->mtx);
1645 1646
1646 return ret; 1647 return ret;
@@ -1783,6 +1784,49 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1783 1784
1784 *cookie = (unsigned long) skb; 1785 *cookie = (unsigned long) skb;
1785 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
1786 /* 1830 /*
1787 * Can transmit right away if the channel was the 1831 * Can transmit right away if the channel was the
1788 * right one and there's no wait involved... If a 1832 * right one and there's no wait involved... If a
@@ -1823,6 +1867,21 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
1823 int ret = -ENOENT; 1867 int ret = -ENOENT;
1824 1868
1825 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
1826 list_for_each_entry(wk, &local->work_list, list) { 1885 list_for_each_entry(wk, &local->work_list, list) {
1827 if (wk->sdata != sdata) 1886 if (wk->sdata != sdata)
1828 continue; 1887 continue;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f866af8de5a..c47d7c0e48a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -953,10 +953,12 @@ struct ieee80211_local {
953 953
954 struct ieee80211_channel *hw_roc_channel; 954 struct ieee80211_channel *hw_roc_channel;
955 struct net_device *hw_roc_dev; 955 struct net_device *hw_roc_dev;
956 struct sk_buff *hw_roc_skb;
956 struct work_struct hw_roc_start, hw_roc_done; 957 struct work_struct hw_roc_start, hw_roc_done;
957 enum nl80211_channel_type hw_roc_channel_type; 958 enum nl80211_channel_type hw_roc_channel_type;
958 unsigned int hw_roc_duration; 959 unsigned int hw_roc_duration;
959 u32 hw_roc_cookie; 960 u32 hw_roc_cookie;
961 bool hw_roc_for_tx;
960 962
961 /* dummy netdev for use w/ NAPI */ 963 /* dummy netdev for use w/ NAPI */
962 struct net_device napi_dev; 964 struct net_device napi_dev;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 49b9ec22d9b..b4e52676f3f 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -196,6 +196,7 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
196{ 196{
197 struct ieee80211_local *local = 197 struct ieee80211_local *local =
198 container_of(work, struct ieee80211_local, hw_roc_start); 198 container_of(work, struct ieee80211_local, hw_roc_start);
199 struct ieee80211_sub_if_data *sdata;
199 200
200 mutex_lock(&local->mtx); 201 mutex_lock(&local->mtx);
201 202
@@ -206,11 +207,19 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
206 207
207 ieee80211_recalc_idle(local); 208 ieee80211_recalc_idle(local);
208 209
209 cfg80211_ready_on_channel(local->hw_roc_dev, local->hw_roc_cookie, 210 if (local->hw_roc_skb) {
210 local->hw_roc_channel, 211 sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
211 local->hw_roc_channel_type, 212 ieee80211_tx_skb(sdata, local->hw_roc_skb);
212 local->hw_roc_duration, 213 local->hw_roc_skb = NULL;
213 GFP_KERNEL); 214 } else {
215 cfg80211_ready_on_channel(local->hw_roc_dev,
216 local->hw_roc_cookie,
217 local->hw_roc_channel,
218 local->hw_roc_channel_type,
219 local->hw_roc_duration,
220 GFP_KERNEL);
221 }
222
214 mutex_unlock(&local->mtx); 223 mutex_unlock(&local->mtx);
215} 224}
216 225
@@ -236,11 +245,12 @@ static void ieee80211_hw_roc_done(struct work_struct *work)
236 return; 245 return;
237 } 246 }
238 247
239 cfg80211_remain_on_channel_expired(local->hw_roc_dev, 248 if (!local->hw_roc_for_tx)
240 local->hw_roc_cookie, 249 cfg80211_remain_on_channel_expired(local->hw_roc_dev,
241 local->hw_roc_channel, 250 local->hw_roc_cookie,
242 local->hw_roc_channel_type, 251 local->hw_roc_channel,
243 GFP_KERNEL); 252 local->hw_roc_channel_type,
253 GFP_KERNEL);
244 254
245 local->hw_roc_channel = NULL; 255 local->hw_roc_channel = NULL;
246 local->hw_roc_cookie = 0; 256 local->hw_roc_cookie = 0;