Commit 1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3
Committed by
John W. Linville
1 parent
6081162e2e
Exists in
master
and in
20 other branches
cfg80211: add rfkill support
To be easier on drivers and users, have cfg80211 register an rfkill structure that drivers can access. When soft-killed, simply take down all interfaces; when hard-killed the driver needs to notify us and we will take down the interfaces after the fact. While rfkilled, interfaces cannot be set UP. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Showing 11 changed files with 172 additions and 30 deletions Side-by-side Diff
include/asm-generic/errno.h
include/net/cfg80211.h
... | ... | @@ -757,13 +757,11 @@ |
757 | 757 | * @TX_POWER_AUTOMATIC: the dbm parameter is ignored |
758 | 758 | * @TX_POWER_LIMITED: limit TX power by the dbm parameter |
759 | 759 | * @TX_POWER_FIXED: fix TX power to the dbm parameter |
760 | - * @TX_POWER_OFF: turn off completely (will go away) | |
761 | 760 | */ |
762 | 761 | enum tx_power_setting { |
763 | 762 | TX_POWER_AUTOMATIC, |
764 | 763 | TX_POWER_LIMITED, |
765 | 764 | TX_POWER_FIXED, |
766 | - TX_POWER_OFF, | |
767 | 765 | }; |
768 | 766 | |
769 | 767 | /** |
... | ... | @@ -855,8 +853,10 @@ |
855 | 853 | * |
856 | 854 | * @set_tx_power: set the transmit power according to the parameters |
857 | 855 | * @get_tx_power: store the current TX power into the dbm variable; |
858 | - * return 0 if successful; or -ENETDOWN if successful but power | |
859 | - * is disabled (this will go away) | |
856 | + * return 0 if successful | |
857 | + * | |
858 | + * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting | |
859 | + * functions to adjust rfkill hw state | |
860 | 860 | */ |
861 | 861 | struct cfg80211_ops { |
862 | 862 | int (*suspend)(struct wiphy *wiphy); |
... | ... | @@ -952,6 +952,8 @@ |
952 | 952 | int (*set_tx_power)(struct wiphy *wiphy, |
953 | 953 | enum tx_power_setting type, int dbm); |
954 | 954 | int (*get_tx_power)(struct wiphy *wiphy, int *dbm); |
955 | + | |
956 | + void (*rfkill_poll)(struct wiphy *wiphy); | |
955 | 957 | }; |
956 | 958 | |
957 | 959 | /* |
... | ... | @@ -1665,6 +1667,25 @@ |
1665 | 1667 | * always a scan result for this IBSS. cfg80211 will handle the rest. |
1666 | 1668 | */ |
1667 | 1669 | void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); |
1670 | + | |
1671 | +/** | |
1672 | + * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state | |
1673 | + * @wiphy: the wiphy | |
1674 | + * @blocked: block status | |
1675 | + */ | |
1676 | +void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked); | |
1677 | + | |
1678 | +/** | |
1679 | + * wiphy_rfkill_start_polling - start polling rfkill | |
1680 | + * @wiphy: the wiphy | |
1681 | + */ | |
1682 | +void wiphy_rfkill_start_polling(struct wiphy *wiphy); | |
1683 | + | |
1684 | +/** | |
1685 | + * wiphy_rfkill_stop_polling - stop polling rfkill | |
1686 | + * @wiphy: the wiphy | |
1687 | + */ | |
1688 | +void wiphy_rfkill_stop_polling(struct wiphy *wiphy); | |
1668 | 1689 | |
1669 | 1690 | #endif /* __NET_CFG80211_H */ |
include/net/mac80211.h
... | ... | @@ -526,7 +526,7 @@ |
526 | 526 | /** |
527 | 527 | * enum ieee80211_conf_changed - denotes which configuration changed |
528 | 528 | * |
529 | - * @IEEE80211_CONF_CHANGE_RADIO_ENABLED: the value of radio_enabled changed | |
529 | + * @_IEEE80211_CONF_CHANGE_RADIO_ENABLED: DEPRECATED | |
530 | 530 | * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed |
531 | 531 | * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed |
532 | 532 | * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed |
... | ... | @@ -536,7 +536,7 @@ |
536 | 536 | * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed |
537 | 537 | */ |
538 | 538 | enum ieee80211_conf_changed { |
539 | - IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0), | |
539 | + _IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0), | |
540 | 540 | IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), |
541 | 541 | IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3), |
542 | 542 | IEEE80211_CONF_CHANGE_PS = BIT(4), |
... | ... | @@ -546,6 +546,14 @@ |
546 | 546 | IEEE80211_CONF_CHANGE_IDLE = BIT(8), |
547 | 547 | }; |
548 | 548 | |
549 | +static inline __deprecated enum ieee80211_conf_changed | |
550 | +__IEEE80211_CONF_CHANGE_RADIO_ENABLED(void) | |
551 | +{ | |
552 | + return _IEEE80211_CONF_CHANGE_RADIO_ENABLED; | |
553 | +} | |
554 | +#define IEEE80211_CONF_CHANGE_RADIO_ENABLED \ | |
555 | + __IEEE80211_CONF_CHANGE_RADIO_ENABLED() | |
556 | + | |
549 | 557 | /** |
550 | 558 | * struct ieee80211_conf - configuration of the device |
551 | 559 | * |
... | ... | @@ -585,7 +593,7 @@ |
585 | 593 | int max_sleep_period; |
586 | 594 | |
587 | 595 | u16 listen_interval; |
588 | - bool radio_enabled; | |
596 | + bool __deprecated radio_enabled; | |
589 | 597 | |
590 | 598 | u8 long_frame_max_tx_count, short_frame_max_tx_count; |
591 | 599 | |
... | ... | @@ -1396,6 +1404,10 @@ |
1396 | 1404 | * is the first frame we expect to perform the action on. Notice |
1397 | 1405 | * that TX/RX_STOP can pass NULL for this parameter. |
1398 | 1406 | * Returns a negative error code on failure. |
1407 | + * | |
1408 | + * @rfkill_poll: Poll rfkill hardware state. If you need this, you also | |
1409 | + * need to set wiphy->rfkill_poll to %true before registration, | |
1410 | + * and need to call wiphy_rfkill_set_hw_state() in the callback. | |
1399 | 1411 | */ |
1400 | 1412 | struct ieee80211_ops { |
1401 | 1413 | int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); |
... | ... | @@ -1444,6 +1456,8 @@ |
1444 | 1456 | int (*ampdu_action)(struct ieee80211_hw *hw, |
1445 | 1457 | enum ieee80211_ampdu_mlme_action action, |
1446 | 1458 | struct ieee80211_sta *sta, u16 tid, u16 *ssn); |
1459 | + | |
1460 | + void (*rfkill_poll)(struct ieee80211_hw *hw); | |
1447 | 1461 | }; |
1448 | 1462 | |
1449 | 1463 | /** |
net/mac80211/cfg.c
... | ... | @@ -1340,7 +1340,6 @@ |
1340 | 1340 | struct ieee80211_local *local = wiphy_priv(wiphy); |
1341 | 1341 | struct ieee80211_channel *chan = local->hw.conf.channel; |
1342 | 1342 | u32 changes = 0; |
1343 | - bool radio_enabled = true; | |
1344 | 1343 | |
1345 | 1344 | switch (type) { |
1346 | 1345 | case TX_POWER_AUTOMATIC: |
1347 | 1346 | |
... | ... | @@ -1359,16 +1358,8 @@ |
1359 | 1358 | return -EINVAL; |
1360 | 1359 | local->user_power_level = dbm; |
1361 | 1360 | break; |
1362 | - case TX_POWER_OFF: | |
1363 | - radio_enabled = false; | |
1364 | - break; | |
1365 | 1361 | } |
1366 | 1362 | |
1367 | - if (radio_enabled != local->hw.conf.radio_enabled) { | |
1368 | - changes |= IEEE80211_CONF_CHANGE_RADIO_ENABLED; | |
1369 | - local->hw.conf.radio_enabled = radio_enabled; | |
1370 | - } | |
1371 | - | |
1372 | 1363 | ieee80211_hw_config(local, changes); |
1373 | 1364 | |
1374 | 1365 | return 0; |
1375 | 1366 | |
... | ... | @@ -1380,12 +1371,16 @@ |
1380 | 1371 | |
1381 | 1372 | *dbm = local->hw.conf.power_level; |
1382 | 1373 | |
1383 | - if (!local->hw.conf.radio_enabled) | |
1384 | - return -ENETDOWN; | |
1385 | - | |
1386 | 1374 | return 0; |
1387 | 1375 | } |
1388 | 1376 | |
1377 | +static void ieee80211_rfkill_poll(struct wiphy *wiphy) | |
1378 | +{ | |
1379 | + struct ieee80211_local *local = wiphy_priv(wiphy); | |
1380 | + | |
1381 | + drv_rfkill_poll(local); | |
1382 | +} | |
1383 | + | |
1389 | 1384 | struct cfg80211_ops mac80211_config_ops = { |
1390 | 1385 | .add_virtual_intf = ieee80211_add_iface, |
1391 | 1386 | .del_virtual_intf = ieee80211_del_iface, |
... | ... | @@ -1427,5 +1422,6 @@ |
1427 | 1422 | .set_wiphy_params = ieee80211_set_wiphy_params, |
1428 | 1423 | .set_tx_power = ieee80211_set_tx_power, |
1429 | 1424 | .get_tx_power = ieee80211_get_tx_power, |
1425 | + .rfkill_poll = ieee80211_rfkill_poll, | |
1430 | 1426 | }; |
net/mac80211/driver-ops.h
... | ... | @@ -181,5 +181,12 @@ |
181 | 181 | sta, tid, ssn); |
182 | 182 | return -EOPNOTSUPP; |
183 | 183 | } |
184 | + | |
185 | + | |
186 | +static inline void drv_rfkill_poll(struct ieee80211_local *local) | |
187 | +{ | |
188 | + if (local->ops->rfkill_poll) | |
189 | + local->ops->rfkill_poll(&local->hw); | |
190 | +} | |
184 | 191 | #endif /* __MAC80211_DRIVER_OPS */ |
net/mac80211/iface.c
... | ... | @@ -170,7 +170,7 @@ |
170 | 170 | goto err_del_bss; |
171 | 171 | /* we're brought up, everything changes */ |
172 | 172 | hw_reconf_flags = ~0; |
173 | - ieee80211_led_radio(local, local->hw.conf.radio_enabled); | |
173 | + ieee80211_led_radio(local, true); | |
174 | 174 | } |
175 | 175 | |
176 | 176 | /* |
... | ... | @@ -560,7 +560,7 @@ |
560 | 560 | |
561 | 561 | drv_stop(local); |
562 | 562 | |
563 | - ieee80211_led_radio(local, 0); | |
563 | + ieee80211_led_radio(local, false); | |
564 | 564 | |
565 | 565 | flush_workqueue(local->hw.workqueue); |
566 | 566 |
net/mac80211/util.c
net/wireless/Kconfig
net/wireless/core.c
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | #include <linux/debugfs.h> |
13 | 13 | #include <linux/notifier.h> |
14 | 14 | #include <linux/device.h> |
15 | +#include <linux/rtnetlink.h> | |
15 | 16 | #include <net/genetlink.h> |
16 | 17 | #include <net/cfg80211.h> |
17 | 18 | #include "nl80211.h" |
... | ... | @@ -227,6 +228,41 @@ |
227 | 228 | return 0; |
228 | 229 | } |
229 | 230 | |
231 | +static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) | |
232 | +{ | |
233 | + struct cfg80211_registered_device *drv = data; | |
234 | + | |
235 | + drv->ops->rfkill_poll(&drv->wiphy); | |
236 | +} | |
237 | + | |
238 | +static int cfg80211_rfkill_set_block(void *data, bool blocked) | |
239 | +{ | |
240 | + struct cfg80211_registered_device *drv = data; | |
241 | + struct wireless_dev *wdev; | |
242 | + | |
243 | + if (!blocked) | |
244 | + return 0; | |
245 | + | |
246 | + rtnl_lock(); | |
247 | + mutex_lock(&drv->devlist_mtx); | |
248 | + | |
249 | + list_for_each_entry(wdev, &drv->netdev_list, list) | |
250 | + dev_close(wdev->netdev); | |
251 | + | |
252 | + mutex_unlock(&drv->devlist_mtx); | |
253 | + rtnl_unlock(); | |
254 | + | |
255 | + return 0; | |
256 | +} | |
257 | + | |
258 | +static void cfg80211_rfkill_sync_work(struct work_struct *work) | |
259 | +{ | |
260 | + struct cfg80211_registered_device *drv; | |
261 | + | |
262 | + drv = container_of(work, struct cfg80211_registered_device, rfkill_sync); | |
263 | + cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); | |
264 | +} | |
265 | + | |
230 | 266 | /* exported functions */ |
231 | 267 | |
232 | 268 | struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) |
... | ... | @@ -274,6 +310,18 @@ |
274 | 310 | drv->wiphy.dev.class = &ieee80211_class; |
275 | 311 | drv->wiphy.dev.platform_data = drv; |
276 | 312 | |
313 | + drv->rfkill_ops.set_block = cfg80211_rfkill_set_block; | |
314 | + drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev), | |
315 | + &drv->wiphy.dev, RFKILL_TYPE_WLAN, | |
316 | + &drv->rfkill_ops, drv); | |
317 | + | |
318 | + if (!drv->rfkill) { | |
319 | + kfree(drv); | |
320 | + return NULL; | |
321 | + } | |
322 | + | |
323 | + INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); | |
324 | + | |
277 | 325 | /* |
278 | 326 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. |
279 | 327 | * Fragmentation and RTS threshold are disabled by default with the |
... | ... | @@ -356,6 +404,10 @@ |
356 | 404 | if (res) |
357 | 405 | goto out_unlock; |
358 | 406 | |
407 | + res = rfkill_register(drv->rfkill); | |
408 | + if (res) | |
409 | + goto out_rm_dev; | |
410 | + | |
359 | 411 | list_add(&drv->list, &cfg80211_drv_list); |
360 | 412 | |
361 | 413 | /* add to debugfs */ |
362 | 414 | |
363 | 415 | |
... | ... | @@ -379,16 +431,41 @@ |
379 | 431 | cfg80211_debugfs_drv_add(drv); |
380 | 432 | |
381 | 433 | res = 0; |
382 | -out_unlock: | |
434 | + goto out_unlock; | |
435 | + | |
436 | + out_rm_dev: | |
437 | + device_del(&drv->wiphy.dev); | |
438 | + out_unlock: | |
383 | 439 | mutex_unlock(&cfg80211_mutex); |
384 | 440 | return res; |
385 | 441 | } |
386 | 442 | EXPORT_SYMBOL(wiphy_register); |
387 | 443 | |
444 | +void wiphy_rfkill_start_polling(struct wiphy *wiphy) | |
445 | +{ | |
446 | + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); | |
447 | + | |
448 | + if (!drv->ops->rfkill_poll) | |
449 | + return; | |
450 | + drv->rfkill_ops.poll = cfg80211_rfkill_poll; | |
451 | + rfkill_resume_polling(drv->rfkill); | |
452 | +} | |
453 | +EXPORT_SYMBOL(wiphy_rfkill_start_polling); | |
454 | + | |
455 | +void wiphy_rfkill_stop_polling(struct wiphy *wiphy) | |
456 | +{ | |
457 | + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); | |
458 | + | |
459 | + rfkill_pause_polling(drv->rfkill); | |
460 | +} | |
461 | +EXPORT_SYMBOL(wiphy_rfkill_stop_polling); | |
462 | + | |
388 | 463 | void wiphy_unregister(struct wiphy *wiphy) |
389 | 464 | { |
390 | 465 | struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); |
391 | 466 | |
467 | + rfkill_unregister(drv->rfkill); | |
468 | + | |
392 | 469 | /* protect the device list */ |
393 | 470 | mutex_lock(&cfg80211_mutex); |
394 | 471 | |
... | ... | @@ -425,6 +502,7 @@ |
425 | 502 | void cfg80211_dev_free(struct cfg80211_registered_device *drv) |
426 | 503 | { |
427 | 504 | struct cfg80211_internal_bss *scan, *tmp; |
505 | + rfkill_destroy(drv->rfkill); | |
428 | 506 | mutex_destroy(&drv->mtx); |
429 | 507 | mutex_destroy(&drv->devlist_mtx); |
430 | 508 | list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) |
... | ... | @@ -438,6 +516,15 @@ |
438 | 516 | } |
439 | 517 | EXPORT_SYMBOL(wiphy_free); |
440 | 518 | |
519 | +void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) | |
520 | +{ | |
521 | + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); | |
522 | + | |
523 | + if (rfkill_set_hw_state(drv->rfkill, blocked)) | |
524 | + schedule_work(&drv->rfkill_sync); | |
525 | +} | |
526 | +EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); | |
527 | + | |
441 | 528 | static int cfg80211_netdev_notifier_call(struct notifier_block * nb, |
442 | 529 | unsigned long state, |
443 | 530 | void *ndev) |
... | ... | @@ -446,7 +533,7 @@ |
446 | 533 | struct cfg80211_registered_device *rdev; |
447 | 534 | |
448 | 535 | if (!dev->ieee80211_ptr) |
449 | - return 0; | |
536 | + return NOTIFY_DONE; | |
450 | 537 | |
451 | 538 | rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); |
452 | 539 | |
453 | 540 | |
... | ... | @@ -492,9 +579,13 @@ |
492 | 579 | } |
493 | 580 | mutex_unlock(&rdev->devlist_mtx); |
494 | 581 | break; |
582 | + case NETDEV_PRE_UP: | |
583 | + if (rfkill_blocked(rdev->rfkill)) | |
584 | + return notifier_from_errno(-ERFKILL); | |
585 | + break; | |
495 | 586 | } |
496 | 587 | |
497 | - return 0; | |
588 | + return NOTIFY_DONE; | |
498 | 589 | } |
499 | 590 | |
500 | 591 | static struct notifier_block cfg80211_netdev_notifier = { |
net/wireless/core.h
... | ... | @@ -11,6 +11,8 @@ |
11 | 11 | #include <linux/kref.h> |
12 | 12 | #include <linux/rbtree.h> |
13 | 13 | #include <linux/debugfs.h> |
14 | +#include <linux/rfkill.h> | |
15 | +#include <linux/workqueue.h> | |
14 | 16 | #include <net/genetlink.h> |
15 | 17 | #include <net/cfg80211.h> |
16 | 18 | #include "reg.h" |
... | ... | @@ -23,6 +25,11 @@ |
23 | 25 | * to avoid the deregister call to proceed while |
24 | 26 | * any call is in progress */ |
25 | 27 | struct mutex mtx; |
28 | + | |
29 | + /* rfkill support */ | |
30 | + struct rfkill_ops rfkill_ops; | |
31 | + struct rfkill *rfkill; | |
32 | + struct work_struct rfkill_sync; | |
26 | 33 | |
27 | 34 | /* ISO / IEC 3166 alpha2 for which this device is receiving |
28 | 35 | * country IEs on, this can help disregard country IEs from APs |
net/wireless/wext-compat.c
... | ... | @@ -764,6 +764,8 @@ |
764 | 764 | |
765 | 765 | /* only change when not disabling */ |
766 | 766 | if (!data->txpower.disabled) { |
767 | + rfkill_set_sw_state(rdev->rfkill, false); | |
768 | + | |
767 | 769 | if (data->txpower.fixed) { |
768 | 770 | /* |
769 | 771 | * wext doesn't support negative values, see |
... | ... | @@ -787,7 +789,9 @@ |
787 | 789 | } |
788 | 790 | } |
789 | 791 | } else { |
790 | - type = TX_POWER_OFF; | |
792 | + rfkill_set_sw_state(rdev->rfkill, true); | |
793 | + schedule_work(&rdev->rfkill_sync); | |
794 | + return 0; | |
791 | 795 | } |
792 | 796 | |
793 | 797 | return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);; |
794 | 798 | |
... | ... | @@ -811,13 +815,12 @@ |
811 | 815 | return -EOPNOTSUPP; |
812 | 816 | |
813 | 817 | err = rdev->ops->get_tx_power(wdev->wiphy, &val); |
814 | - /* HACK!!! */ | |
815 | - if (err && err != -ENETDOWN) | |
818 | + if (err) | |
816 | 819 | return err; |
817 | 820 | |
818 | 821 | /* well... oh well */ |
819 | 822 | data->txpower.fixed = 1; |
820 | - data->txpower.disabled = err == -ENETDOWN; | |
823 | + data->txpower.disabled = rfkill_blocked(rdev->rfkill); | |
821 | 824 | data->txpower.value = val; |
822 | 825 | data->txpower.flags = IW_TXPOW_DBM; |
823 | 826 |