Commit 1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3

Authored by Johannes Berg
Committed by John W. Linville
1 parent 6081162e2e

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
... ... @@ -106,5 +106,7 @@
106 106 #define EOWNERDEAD 130 /* Owner died */
107 107 #define ENOTRECOVERABLE 131 /* State not recoverable */
108 108  
  109 +#define ERFKILL 132 /* Operation not possible due to RF-kill */
  110 +
109 111 #endif
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 /**
... ... @@ -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  
... ... @@ -973,7 +973,7 @@
973 973 if (local->open_count) {
974 974 res = drv_start(local);
975 975  
976   - ieee80211_led_radio(local, hw->conf.radio_enabled);
  976 + ieee80211_led_radio(local, true);
977 977 }
978 978  
979 979 /* add interfaces */
net/wireless/Kconfig
1 1 config CFG80211
2   - tristate "Improved wireless configuration API"
  2 + tristate "Improved wireless configuration API"
  3 + depends on RFKILL || !RFKILL
3 4  
4 5 config CFG80211_REG_DEBUG
5 6 bool "cfg80211 regulatory debugging"
... ... @@ -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 = {
... ... @@ -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