aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-02-25 09:36:57 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-02-25 15:33:40 -0500
commit5f16a43617d46cf255a66f4dc193a7f5b2540aaf (patch)
treeadd5e551c9d1abeee57b1fbad632a0e23bdd39b1
parent8628172f45c839376bf2b70bbd326d56e68dadc3 (diff)
mac80211: support direct offchannel TX offload
For devices supported by iwlwifi sometimes off-channel transmissions need to be handled by the device completely. To support this mac80211 needs to pass the frame directly to the driver and not through the TX path as the driver needs the frame and channel information at the same time. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/net/mac80211.h10
-rw-r--r--net/mac80211/cfg.c39
-rw-r--r--net/mac80211/driver-ops.h31
-rw-r--r--net/mac80211/driver-trace.h33
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/status.c4
6 files changed, 118 insertions, 0 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 96cc7ed35169..2b072fa99399 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1799,6 +1799,11 @@ enum ieee80211_ampdu_mlme_action {
1799 * ieee80211_remain_on_channel_expired(). This callback may sleep. 1799 * ieee80211_remain_on_channel_expired(). This callback may sleep.
1800 * @cancel_remain_on_channel: Requests that an ongoing off-channel period is 1800 * @cancel_remain_on_channel: Requests that an ongoing off-channel period is
1801 * aborted before it expires. This callback may sleep. 1801 * aborted before it expires. This callback may sleep.
1802 * @offchannel_tx: Transmit frame on another channel, wait for a response
1803 * and return. Reliable TX status must be reported for the frame. If the
1804 * return value is 1, then the @remain_on_channel will be used with a
1805 * regular transmission (if supported.)
1806 * @offchannel_tx_cancel_wait: cancel wait associated with offchannel TX
1802 */ 1807 */
1803struct ieee80211_ops { 1808struct ieee80211_ops {
1804 void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); 1809 void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -1878,6 +1883,11 @@ struct ieee80211_ops {
1878 enum nl80211_channel_type channel_type, 1883 enum nl80211_channel_type channel_type,
1879 int duration); 1884 int duration);
1880 int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); 1885 int (*cancel_remain_on_channel)(struct ieee80211_hw *hw);
1886 int (*offchannel_tx)(struct ieee80211_hw *hw, struct sk_buff *skb,
1887 struct ieee80211_channel *chan,
1888 enum nl80211_channel_type channel_type,
1889 unsigned int wait);
1890 int (*offchannel_tx_cancel_wait)(struct ieee80211_hw *hw);
1881}; 1891};
1882 1892
1883/** 1893/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 140503d4c97a..8b436c768c4e 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1800,6 +1800,33 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1800 1800
1801 *cookie = (unsigned long) skb; 1801 *cookie = (unsigned long) skb;
1802 1802
1803 if (is_offchan && local->ops->offchannel_tx) {
1804 int ret;
1805
1806 IEEE80211_SKB_CB(skb)->band = chan->band;
1807
1808 mutex_lock(&local->mtx);
1809
1810 if (local->hw_offchan_tx_cookie) {
1811 mutex_unlock(&local->mtx);
1812 return -EBUSY;
1813 }
1814
1815 /* TODO: bitrate control, TX processing? */
1816 ret = drv_offchannel_tx(local, skb, chan, channel_type, wait);
1817
1818 if (ret == 0)
1819 local->hw_offchan_tx_cookie = *cookie;
1820 mutex_unlock(&local->mtx);
1821
1822 /*
1823 * Allow driver to return 1 to indicate it wants to have the
1824 * frame transmitted with a remain_on_channel + regular TX.
1825 */
1826 if (ret != 1)
1827 return ret;
1828 }
1829
1803 if (is_offchan && local->ops->remain_on_channel) { 1830 if (is_offchan && local->ops->remain_on_channel) {
1804 unsigned int duration; 1831 unsigned int duration;
1805 int ret; 1832 int ret;
@@ -1886,6 +1913,18 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
1886 1913
1887 mutex_lock(&local->mtx); 1914 mutex_lock(&local->mtx);
1888 1915
1916 if (local->ops->offchannel_tx_cancel_wait &&
1917 local->hw_offchan_tx_cookie == cookie) {
1918 ret = drv_offchannel_tx_cancel_wait(local);
1919
1920 if (!ret)
1921 local->hw_offchan_tx_cookie = 0;
1922
1923 mutex_unlock(&local->mtx);
1924
1925 return ret;
1926 }
1927
1889 if (local->ops->cancel_remain_on_channel) { 1928 if (local->ops->cancel_remain_on_channel) {
1890 cookie ^= 2; 1929 cookie ^= 2;
1891 ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); 1930 ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 32f05c1abbaf..3729296f6f95 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -495,4 +495,35 @@ static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local)
495 return ret; 495 return ret;
496} 496}
497 497
498static inline int drv_offchannel_tx(struct ieee80211_local *local,
499 struct sk_buff *skb,
500 struct ieee80211_channel *chan,
501 enum nl80211_channel_type channel_type,
502 unsigned int wait)
503{
504 int ret;
505
506 might_sleep();
507
508 trace_drv_offchannel_tx(local, skb, chan, channel_type, wait);
509 ret = local->ops->offchannel_tx(&local->hw, skb, chan,
510 channel_type, wait);
511 trace_drv_return_int(local, ret);
512
513 return ret;
514}
515
516static inline int drv_offchannel_tx_cancel_wait(struct ieee80211_local *local)
517{
518 int ret;
519
520 might_sleep();
521
522 trace_drv_offchannel_tx_cancel_wait(local);
523 ret = local->ops->offchannel_tx_cancel_wait(&local->hw);
524 trace_drv_return_int(local, ret);
525
526 return ret;
527}
528
498#endif /* __MAC80211_DRIVER_OPS */ 529#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index e5cce19a7d65..520fe2444893 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -884,6 +884,39 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
884 TP_ARGS(local) 884 TP_ARGS(local)
885); 885);
886 886
887TRACE_EVENT(drv_offchannel_tx,
888 TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
889 struct ieee80211_channel *chan,
890 enum nl80211_channel_type channel_type,
891 unsigned int wait),
892
893 TP_ARGS(local, skb, chan, channel_type, wait),
894
895 TP_STRUCT__entry(
896 LOCAL_ENTRY
897 __field(int, center_freq)
898 __field(int, channel_type)
899 __field(unsigned int, wait)
900 ),
901
902 TP_fast_assign(
903 LOCAL_ASSIGN;
904 __entry->center_freq = chan->center_freq;
905 __entry->channel_type = channel_type;
906 __entry->wait = wait;
907 ),
908
909 TP_printk(
910 LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
911 LOCAL_PR_ARG, __entry->center_freq, __entry->wait
912 )
913);
914
915DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait,
916 TP_PROTO(struct ieee80211_local *local),
917 TP_ARGS(local)
918);
919
887/* 920/*
888 * Tracing for API calls that drivers call. 921 * Tracing for API calls that drivers call.
889 */ 922 */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0a570a111a84..a40401701424 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -957,6 +957,7 @@ struct ieee80211_local {
957 unsigned int hw_roc_duration; 957 unsigned int hw_roc_duration;
958 u32 hw_roc_cookie; 958 u32 hw_roc_cookie;
959 bool hw_roc_for_tx; 959 bool hw_roc_for_tx;
960 unsigned long hw_offchan_tx_cookie;
960 961
961 /* dummy netdev for use w/ NAPI */ 962 /* dummy netdev for use w/ NAPI */
962 struct net_device napi_dev; 963 struct net_device napi_dev;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 865185127f51..b936dd29e92b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -341,6 +341,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
341 cookie = local->hw_roc_cookie ^ 2; 341 cookie = local->hw_roc_cookie ^ 2;
342 local->hw_roc_skb_for_status = NULL; 342 local->hw_roc_skb_for_status = NULL;
343 } 343 }
344
345 if (cookie == local->hw_offchan_tx_cookie)
346 local->hw_offchan_tx_cookie = 0;
347
344 cfg80211_mgmt_tx_status( 348 cfg80211_mgmt_tx_status(
345 skb->dev, cookie, skb->data, skb->len, 349 skb->dev, cookie, skb->data, skb->len,
346 !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); 350 !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);