diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-12-18 11:20:47 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-05 16:07:12 -0500 |
commit | 21f83589644bb2ed98079bf1e2154c8e70ca6a6c (patch) | |
tree | ede391a8c788a43f7c4ea3baa3367e020d45f179 /net | |
parent | c96e96354a6c9456cdf1f150eca504e2ea35301e (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.c | 83 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 30 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 80 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 8 | ||||
-rw-r--r-- | net/mac80211/iface.c | 9 | ||||
-rw-r--r-- | net/mac80211/main.c | 5 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 75 |
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 | ||
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,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 | ||
1653 | static 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 | |||
1609 | static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, | 1675 | static 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 | ||
468 | static 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 | |||
485 | static 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 | ||
936 | TRACE_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 | |||
962 | TRACE_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 | ||
1217 | TRACE_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 | |||
1235 | TRACE_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); | |||
1142 | void ieee80211_offchannel_stop_station(struct ieee80211_local *local); | 1149 | void ieee80211_offchannel_stop_station(struct ieee80211_local *local); |
1143 | void ieee80211_offchannel_return(struct ieee80211_local *local, | 1150 | void ieee80211_offchannel_return(struct ieee80211_local *local, |
1144 | bool enable_beaconing); | 1151 | bool enable_beaconing); |
1152 | void ieee80211_hw_roc_setup(struct ieee80211_local *local); | ||
1145 | 1153 | ||
1146 | /* interface handling */ | 1154 | /* interface handling */ |
1147 | int ieee80211_iface_init(void); | 1155 | int 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 | } |
614 | EXPORT_SYMBOL(ieee80211_alloc_hw); | 616 | EXPORT_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 | |||
195 | static 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 | |||
217 | void 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 | } | ||
225 | EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); | ||
226 | |||
227 | static 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 | |||
253 | void 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 | } | ||
261 | EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); | ||
262 | |||
263 | void 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 | } | ||