aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-12-18 11:20:47 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-01-05 16:07:12 -0500
commit21f83589644bb2ed98079bf1e2154c8e70ca6a6c (patch)
treeede391a8c788a43f7c4ea3baa3367e020d45f179 /net
parentc96e96354a6c9456cdf1f150eca504e2ea35301e (diff)
mac80211: implement hardware offload for remain-on-channel
This allows drivers to support remain-on-channel offload if they implement smarter timing or need to use a device implementation like iwlwifi. 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.c83
-rw-r--r--net/mac80211/driver-ops.h30
-rw-r--r--net/mac80211/driver-trace.h80
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/iface.c9
-rw-r--r--net/mac80211/main.c5
-rw-r--r--net/mac80211/offchannel.c75
7 files changed, 287 insertions, 3 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 5892b0302454..168a6ba8fc28 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,62 @@ 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 mutex_unlock(&local->mtx);
1645
1646 return ret;
1647 }
1604 1648
1605 return ieee80211_wk_remain_on_channel(sdata, chan, channel_type, 1649 return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
1606 duration, cookie); 1650 duration, cookie);
1607} 1651}
1608 1652
1653static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local,
1654 u64 cookie)
1655{
1656 int ret;
1657
1658 lockdep_assert_held(&local->mtx);
1659
1660 if (local->hw_roc_cookie != cookie)
1661 return -ENOENT;
1662
1663 ret = drv_cancel_remain_on_channel(local);
1664 if (ret)
1665 return ret;
1666
1667 local->hw_roc_cookie = 0;
1668 local->hw_roc_channel = NULL;
1669
1670 ieee80211_recalc_idle(local);
1671
1672 return 0;
1673}
1674
1609static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, 1675static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
1610 struct net_device *dev, 1676 struct net_device *dev,
1611 u64 cookie) 1677 u64 cookie)
1612{ 1678{
1613 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 1679 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1680 struct ieee80211_local *local = sdata->local;
1681
1682 if (local->ops->cancel_remain_on_channel) {
1683 int ret;
1684
1685 mutex_lock(&local->mtx);
1686 ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
1687 mutex_unlock(&local->mtx);
1688
1689 return ret;
1690 }
1614 1691
1615 return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); 1692 return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
1616} 1693}
@@ -1662,6 +1739,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
1662 channel_type != local->_oper_channel_type)) 1739 channel_type != local->_oper_channel_type))
1663 is_offchan = true; 1740 is_offchan = true;
1664 1741
1742 if (chan == local->hw_roc_channel) {
1743 /* TODO: check channel type? */
1744 is_offchan = false;
1745 flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
1746 }
1747
1665 if (is_offchan && !offchan) 1748 if (is_offchan && !offchan)
1666 return -EBUSY; 1749 return -EBUSY;
1667 1750
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index af0c4398cceb..98d589960a49 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -465,4 +465,34 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
465 return ret; 465 return ret;
466} 466}
467 467
468static inline int drv_remain_on_channel(struct ieee80211_local *local,
469 struct ieee80211_channel *chan,
470 enum nl80211_channel_type chantype,
471 unsigned int duration)
472{
473 int ret;
474
475 might_sleep();
476
477 trace_drv_remain_on_channel(local, chan, chantype, duration);
478 ret = local->ops->remain_on_channel(&local->hw, chan, chantype,
479 duration);
480 trace_drv_return_int(local, ret);
481
482 return ret;
483}
484
485static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local)
486{
487 int ret;
488
489 might_sleep();
490
491 trace_drv_cancel_remain_on_channel(local);
492 ret = local->ops->cancel_remain_on_channel(&local->hw);
493 trace_drv_return_int(local, ret);
494
495 return ret;
496}
497
468#endif /* __MAC80211_DRIVER_OPS */ 498#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index c2772f23ac9c..49c84218b2f4 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -933,6 +933,50 @@ TRACE_EVENT(drv_get_antenna,
933 ) 933 )
934); 934);
935 935
936TRACE_EVENT(drv_remain_on_channel,
937 TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan,
938 enum nl80211_channel_type chantype, unsigned int duration),
939
940 TP_ARGS(local, chan, chantype, duration),
941
942 TP_STRUCT__entry(
943 LOCAL_ENTRY
944 __field(int, center_freq)
945 __field(int, channel_type)
946 __field(unsigned int, duration)
947 ),
948
949 TP_fast_assign(
950 LOCAL_ASSIGN;
951 __entry->center_freq = chan->center_freq;
952 __entry->channel_type = chantype;
953 __entry->duration = duration;
954 ),
955
956 TP_printk(
957 LOCAL_PR_FMT " freq:%dMHz duration:%dms",
958 LOCAL_PR_ARG, __entry->center_freq, __entry->duration
959 )
960);
961
962TRACE_EVENT(drv_cancel_remain_on_channel,
963 TP_PROTO(struct ieee80211_local *local),
964
965 TP_ARGS(local),
966
967 TP_STRUCT__entry(
968 LOCAL_ENTRY
969 ),
970
971 TP_fast_assign(
972 LOCAL_ASSIGN;
973 ),
974
975 TP_printk(
976 LOCAL_PR_FMT, LOCAL_PR_ARG
977 )
978);
979
936/* 980/*
937 * Tracing for API calls that drivers call. 981 * Tracing for API calls that drivers call.
938 */ 982 */
@@ -1170,6 +1214,42 @@ TRACE_EVENT(api_chswitch_done,
1170 ) 1214 )
1171); 1215);
1172 1216
1217TRACE_EVENT(api_ready_on_channel,
1218 TP_PROTO(struct ieee80211_local *local),
1219
1220 TP_ARGS(local),
1221
1222 TP_STRUCT__entry(
1223 LOCAL_ENTRY
1224 ),
1225
1226 TP_fast_assign(
1227 LOCAL_ASSIGN;
1228 ),
1229
1230 TP_printk(
1231 LOCAL_PR_FMT, LOCAL_PR_ARG
1232 )
1233);
1234
1235TRACE_EVENT(api_remain_on_channel_expired,
1236 TP_PROTO(struct ieee80211_local *local),
1237
1238 TP_ARGS(local),
1239
1240 TP_STRUCT__entry(
1241 LOCAL_ENTRY
1242 ),
1243
1244 TP_fast_assign(
1245 LOCAL_ASSIGN;
1246 ),
1247
1248 TP_printk(
1249 LOCAL_PR_FMT, LOCAL_PR_ARG
1250 )
1251);
1252
1173/* 1253/*
1174 * Tracing for internal functions 1254 * Tracing for internal functions
1175 * (which may also be called in response to driver calls) 1255 * (which may also be called in response to driver calls)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 95cdd2a3f809..f866af8de5ac 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -951,6 +951,13 @@ struct ieee80211_local {
951 } debugfs; 951 } debugfs;
952#endif 952#endif
953 953
954 struct ieee80211_channel *hw_roc_channel;
955 struct net_device *hw_roc_dev;
956 struct work_struct hw_roc_start, hw_roc_done;
957 enum nl80211_channel_type hw_roc_channel_type;
958 unsigned int hw_roc_duration;
959 u32 hw_roc_cookie;
960
954 /* dummy netdev for use w/ NAPI */ 961 /* dummy netdev for use w/ NAPI */
955 struct net_device napi_dev; 962 struct net_device napi_dev;
956 963
@@ -1142,6 +1149,7 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
1142void ieee80211_offchannel_stop_station(struct ieee80211_local *local); 1149void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
1143void ieee80211_offchannel_return(struct ieee80211_local *local, 1150void ieee80211_offchannel_return(struct ieee80211_local *local,
1144 bool enable_beaconing); 1151 bool enable_beaconing);
1152void ieee80211_hw_roc_setup(struct ieee80211_local *local);
1145 1153
1146/* interface handling */ 1154/* interface handling */
1147int ieee80211_iface_init(void); 1155int ieee80211_iface_init(void);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b6db237672ff..8acba456744e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1264,7 +1264,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
1264{ 1264{
1265 struct ieee80211_sub_if_data *sdata; 1265 struct ieee80211_sub_if_data *sdata;
1266 int count = 0; 1266 int count = 0;
1267 bool working = false, scanning = false; 1267 bool working = false, scanning = false, hw_roc = false;
1268 struct ieee80211_work *wk; 1268 struct ieee80211_work *wk;
1269 unsigned int led_trig_start = 0, led_trig_stop = 0; 1269 unsigned int led_trig_start = 0, led_trig_stop = 0;
1270 1270
@@ -1308,6 +1308,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
1308 local->scan_sdata->vif.bss_conf.idle = false; 1308 local->scan_sdata->vif.bss_conf.idle = false;
1309 } 1309 }
1310 1310
1311 if (local->hw_roc_channel)
1312 hw_roc = true;
1313
1311 list_for_each_entry(sdata, &local->interfaces, list) { 1314 list_for_each_entry(sdata, &local->interfaces, list) {
1312 if (sdata->old_idle == sdata->vif.bss_conf.idle) 1315 if (sdata->old_idle == sdata->vif.bss_conf.idle)
1313 continue; 1316 continue;
@@ -1316,7 +1319,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
1316 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); 1319 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
1317 } 1320 }
1318 1321
1319 if (working || scanning) 1322 if (working || scanning || hw_roc)
1320 led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK; 1323 led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK;
1321 else 1324 else
1322 led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK; 1325 led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK;
@@ -1328,6 +1331,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
1328 1331
1329 ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop); 1332 ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop);
1330 1333
1334 if (hw_roc)
1335 return ieee80211_idle_off(local, "hw remain-on-channel");
1331 if (working) 1336 if (working)
1332 return ieee80211_idle_off(local, "working"); 1337 return ieee80211_idle_off(local, "working");
1333 if (scanning) 1338 if (scanning)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 4b088b3c25e8..485d36bc9a46 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -609,6 +609,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
609 609
610 ieee80211_led_names(local); 610 ieee80211_led_names(local);
611 611
612 ieee80211_hw_roc_setup(local);
613
612 return local_to_hw(local); 614 return local_to_hw(local);
613} 615}
614EXPORT_SYMBOL(ieee80211_alloc_hw); 616EXPORT_SYMBOL(ieee80211_alloc_hw);
@@ -753,7 +755,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
753 } 755 }
754 } 756 }
755 757
756 local->hw.wiphy->max_remain_on_channel_duration = 5000; 758 if (!local->ops->remain_on_channel)
759 local->hw.wiphy->max_remain_on_channel_duration = 5000;
757 760
758 result = wiphy_register(local->hw.wiphy); 761 result = wiphy_register(local->hw.wiphy);
759 if (result < 0) 762 if (result < 0)
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 4b564091e51d..49b9ec22d9b6 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -14,6 +14,7 @@
14 */ 14 */
15#include <net/mac80211.h> 15#include <net/mac80211.h>
16#include "ieee80211_i.h" 16#include "ieee80211_i.h"
17#include "driver-trace.h"
17 18
18/* 19/*
19 * inform AP that we will go to sleep so that it will buffer the frames 20 * inform AP that we will go to sleep so that it will buffer the frames
@@ -190,3 +191,77 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
190 } 191 }
191 mutex_unlock(&local->iflist_mtx); 192 mutex_unlock(&local->iflist_mtx);
192} 193}
194
195static void ieee80211_hw_roc_start(struct work_struct *work)
196{
197 struct ieee80211_local *local =
198 container_of(work, struct ieee80211_local, hw_roc_start);
199
200 mutex_lock(&local->mtx);
201
202 if (!local->hw_roc_channel) {
203 mutex_unlock(&local->mtx);
204 return;
205 }
206
207 ieee80211_recalc_idle(local);
208
209 cfg80211_ready_on_channel(local->hw_roc_dev, local->hw_roc_cookie,
210 local->hw_roc_channel,
211 local->hw_roc_channel_type,
212 local->hw_roc_duration,
213 GFP_KERNEL);
214 mutex_unlock(&local->mtx);
215}
216
217void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
218{
219 struct ieee80211_local *local = hw_to_local(hw);
220
221 trace_api_ready_on_channel(local);
222
223 ieee80211_queue_work(hw, &local->hw_roc_start);
224}
225EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
226
227static void ieee80211_hw_roc_done(struct work_struct *work)
228{
229 struct ieee80211_local *local =
230 container_of(work, struct ieee80211_local, hw_roc_done);
231
232 mutex_lock(&local->mtx);
233
234 if (!local->hw_roc_channel) {
235 mutex_unlock(&local->mtx);
236 return;
237 }
238
239 cfg80211_remain_on_channel_expired(local->hw_roc_dev,
240 local->hw_roc_cookie,
241 local->hw_roc_channel,
242 local->hw_roc_channel_type,
243 GFP_KERNEL);
244
245 local->hw_roc_channel = NULL;
246 local->hw_roc_cookie = 0;
247
248 ieee80211_recalc_idle(local);
249
250 mutex_unlock(&local->mtx);
251}
252
253void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
254{
255 struct ieee80211_local *local = hw_to_local(hw);
256
257 trace_api_remain_on_channel_expired(local);
258
259 ieee80211_queue_work(hw, &local->hw_roc_done);
260}
261EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
262
263void ieee80211_hw_roc_setup(struct ieee80211_local *local)
264{
265 INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
266 INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
267}