summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-02-25 15:36:57 +0100
committerJohn W. Linville <linville@tuxdriver.com>2011-02-25 15:33:40 -0500
commit5f16a43617d46cf255a66f4dc193a7f5b2540aaf (patch)
treeadd5e551c9d1abeee57b1fbad632a0e23bdd39b1 /net
parent8628172f45c839376bf2b70bbd326d56e68dadc3 (diff)
downloadlinux-5f16a43617d46cf255a66f4dc193a7f5b2540aaf.tar.bz2
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>
Diffstat (limited to 'net')
-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
5 files changed, 108 insertions, 0 deletions
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,
*cookie = (unsigned long) skb;
+ if (is_offchan && local->ops->offchannel_tx) {
+ int ret;
+
+ IEEE80211_SKB_CB(skb)->band = chan->band;
+
+ mutex_lock(&local->mtx);
+
+ if (local->hw_offchan_tx_cookie) {
+ mutex_unlock(&local->mtx);
+ return -EBUSY;
+ }
+
+ /* TODO: bitrate control, TX processing? */
+ ret = drv_offchannel_tx(local, skb, chan, channel_type, wait);
+
+ if (ret == 0)
+ local->hw_offchan_tx_cookie = *cookie;
+ mutex_unlock(&local->mtx);
+
+ /*
+ * Allow driver to return 1 to indicate it wants to have the
+ * frame transmitted with a remain_on_channel + regular TX.
+ */
+ if (ret != 1)
+ return ret;
+ }
+
if (is_offchan && local->ops->remain_on_channel) {
unsigned int duration;
int ret;
@@ -1886,6 +1913,18 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
mutex_lock(&local->mtx);
+ if (local->ops->offchannel_tx_cancel_wait &&
+ local->hw_offchan_tx_cookie == cookie) {
+ ret = drv_offchannel_tx_cancel_wait(local);
+
+ if (!ret)
+ local->hw_offchan_tx_cookie = 0;
+
+ mutex_unlock(&local->mtx);
+
+ return ret;
+ }
+
if (local->ops->cancel_remain_on_channel) {
cookie ^= 2;
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)
return ret;
}
+static inline int drv_offchannel_tx(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ unsigned int wait)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_offchannel_tx(local, skb, chan, channel_type, wait);
+ ret = local->ops->offchannel_tx(&local->hw, skb, chan,
+ channel_type, wait);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline int drv_offchannel_tx_cancel_wait(struct ieee80211_local *local)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_offchannel_tx_cancel_wait(local);
+ ret = local->ops->offchannel_tx_cancel_wait(&local->hw);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
#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,
TP_ARGS(local)
);
+TRACE_EVENT(drv_offchannel_tx,
+ TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ unsigned int wait),
+
+ TP_ARGS(local, skb, chan, channel_type, wait),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, center_freq)
+ __field(int, channel_type)
+ __field(unsigned int, wait)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->center_freq = chan->center_freq;
+ __entry->channel_type = channel_type;
+ __entry->wait = wait;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
+ LOCAL_PR_ARG, __entry->center_freq, __entry->wait
+ )
+);
+
+DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
/*
* Tracing for API calls that drivers call.
*/
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 {
unsigned int hw_roc_duration;
u32 hw_roc_cookie;
bool hw_roc_for_tx;
+ unsigned long hw_offchan_tx_cookie;
/* dummy netdev for use w/ NAPI */
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)
cookie = local->hw_roc_cookie ^ 2;
local->hw_roc_skb_for_status = NULL;
}
+
+ if (cookie == local->hw_offchan_tx_cookie)
+ local->hw_offchan_tx_cookie = 0;
+
cfg80211_mgmt_tx_status(
skb->dev, cookie, skb->data, skb->len,
!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);