Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Johannes Berg and committed by
John W. Linville
1f87f7d3 6081162e

+172 -30
+2
include/asm-generic/errno.h
··· 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
+25 -4
include/net/cfg80211.h
··· 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 /** ··· 853 855 * 854 856 * @set_tx_power: set the transmit power according to the parameters 855 857 * @get_tx_power: store the current TX power into the dbm variable; 856 - * return 0 if successful; or -ENETDOWN if successful but power 857 - * is disabled (this will go away) 858 + * return 0 if successful 859 + * 860 + * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting 861 + * functions to adjust rfkill hw state 858 862 */ 859 863 struct cfg80211_ops { 860 864 int (*suspend)(struct wiphy *wiphy); ··· 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 /* ··· 1667 1665 * always a scan result for this IBSS. cfg80211 will handle the rest. 1668 1666 */ 1669 1667 void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); 1668 + 1669 + /** 1670 + * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state 1671 + * @wiphy: the wiphy 1672 + * @blocked: block status 1673 + */ 1674 + void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked); 1675 + 1676 + /** 1677 + * wiphy_rfkill_start_polling - start polling rfkill 1678 + * @wiphy: the wiphy 1679 + */ 1680 + void wiphy_rfkill_start_polling(struct wiphy *wiphy); 1681 + 1682 + /** 1683 + * wiphy_rfkill_stop_polling - stop polling rfkill 1684 + * @wiphy: the wiphy 1685 + */ 1686 + void wiphy_rfkill_stop_polling(struct wiphy *wiphy); 1670 1687 1671 1688 #endif /* __NET_CFG80211_H */
+17 -3
include/net/mac80211.h
··· 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 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), ··· 545 545 IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7), 546 546 IEEE80211_CONF_CHANGE_IDLE = BIT(8), 547 547 }; 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() 548 556 549 557 /** 550 558 * struct ieee80211_conf - configuration of the device ··· 593 585 int max_sleep_period; 594 586 595 587 u16 listen_interval; 596 - bool radio_enabled; 588 + bool __deprecated radio_enabled; 597 589 598 590 u8 long_frame_max_tx_count, short_frame_max_tx_count; 599 591 ··· 1404 1396 * is the first frame we expect to perform the action on. Notice 1405 1397 * that TX/RX_STOP can pass NULL for this parameter. 1406 1398 * Returns a negative error code on failure. 1399 + * 1400 + * @rfkill_poll: Poll rfkill hardware state. If you need this, you also 1401 + * need to set wiphy->rfkill_poll to %true before registration, 1402 + * and need to call wiphy_rfkill_set_hw_state() in the callback. 1407 1403 */ 1408 1404 struct ieee80211_ops { 1409 1405 int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); ··· 1456 1444 int (*ampdu_action)(struct ieee80211_hw *hw, 1457 1445 enum ieee80211_ampdu_mlme_action action, 1458 1446 struct ieee80211_sta *sta, u16 tid, u16 *ssn); 1447 + 1448 + void (*rfkill_poll)(struct ieee80211_hw *hw); 1459 1449 }; 1460 1450 1461 1451 /**
+8 -12
net/mac80211/cfg.c
··· 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: ··· 1358 1359 return -EINVAL; 1359 1360 local->user_power_level = dbm; 1360 1361 break; 1361 - case TX_POWER_OFF: 1362 - radio_enabled = false; 1363 - break; 1364 - } 1365 - 1366 - if (radio_enabled != local->hw.conf.radio_enabled) { 1367 - changes |= IEEE80211_CONF_CHANGE_RADIO_ENABLED; 1368 - local->hw.conf.radio_enabled = radio_enabled; 1369 1362 } 1370 1363 1371 1364 ieee80211_hw_config(local, changes); ··· 1371 1380 1372 1381 *dbm = local->hw.conf.power_level; 1373 1382 1374 - if (!local->hw.conf.radio_enabled) 1375 - return -ENETDOWN; 1376 - 1377 1383 return 0; 1384 + } 1385 + 1386 + static void ieee80211_rfkill_poll(struct wiphy *wiphy) 1387 + { 1388 + struct ieee80211_local *local = wiphy_priv(wiphy); 1389 + 1390 + drv_rfkill_poll(local); 1378 1391 } 1379 1392 1380 1393 struct cfg80211_ops mac80211_config_ops = { ··· 1422 1427 .set_wiphy_params = ieee80211_set_wiphy_params, 1423 1428 .set_tx_power = ieee80211_set_tx_power, 1424 1429 .get_tx_power = ieee80211_get_tx_power, 1430 + .rfkill_poll = ieee80211_rfkill_poll, 1425 1431 };
+7
net/mac80211/driver-ops.h
··· 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 */
+2 -2
net/mac80211/iface.c
··· 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 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
+1 -1
net/mac80211/util.c
··· 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 */
+2 -1
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"
+94 -3
net/wireless/core.c
··· 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" ··· 228 227 return 0; 229 228 } 230 229 230 + static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) 231 + { 232 + struct cfg80211_registered_device *drv = data; 233 + 234 + drv->ops->rfkill_poll(&drv->wiphy); 235 + } 236 + 237 + static int cfg80211_rfkill_set_block(void *data, bool blocked) 238 + { 239 + struct cfg80211_registered_device *drv = data; 240 + struct wireless_dev *wdev; 241 + 242 + if (!blocked) 243 + return 0; 244 + 245 + rtnl_lock(); 246 + mutex_lock(&drv->devlist_mtx); 247 + 248 + list_for_each_entry(wdev, &drv->netdev_list, list) 249 + dev_close(wdev->netdev); 250 + 251 + mutex_unlock(&drv->devlist_mtx); 252 + rtnl_unlock(); 253 + 254 + return 0; 255 + } 256 + 257 + static void cfg80211_rfkill_sync_work(struct work_struct *work) 258 + { 259 + struct cfg80211_registered_device *drv; 260 + 261 + drv = container_of(work, struct cfg80211_registered_device, rfkill_sync); 262 + cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); 263 + } 264 + 231 265 /* exported functions */ 232 266 233 267 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) ··· 309 273 device_initialize(&drv->wiphy.dev); 310 274 drv->wiphy.dev.class = &ieee80211_class; 311 275 drv->wiphy.dev.platform_data = drv; 276 + 277 + drv->rfkill_ops.set_block = cfg80211_rfkill_set_block; 278 + drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev), 279 + &drv->wiphy.dev, RFKILL_TYPE_WLAN, 280 + &drv->rfkill_ops, drv); 281 + 282 + if (!drv->rfkill) { 283 + kfree(drv); 284 + return NULL; 285 + } 286 + 287 + INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); 312 288 313 289 /* 314 290 * Initialize wiphy parameters to IEEE 802.11 MIB default values. ··· 404 356 if (res) 405 357 goto out_unlock; 406 358 359 + res = rfkill_register(drv->rfkill); 360 + if (res) 361 + goto out_rm_dev; 362 + 407 363 list_add(&drv->list, &cfg80211_drv_list); 408 364 409 365 /* add to debugfs */ ··· 431 379 cfg80211_debugfs_drv_add(drv); 432 380 433 381 res = 0; 434 - out_unlock: 382 + goto out_unlock; 383 + 384 + out_rm_dev: 385 + device_del(&drv->wiphy.dev); 386 + out_unlock: 435 387 mutex_unlock(&cfg80211_mutex); 436 388 return res; 437 389 } 438 390 EXPORT_SYMBOL(wiphy_register); 439 391 392 + void wiphy_rfkill_start_polling(struct wiphy *wiphy) 393 + { 394 + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); 395 + 396 + if (!drv->ops->rfkill_poll) 397 + return; 398 + drv->rfkill_ops.poll = cfg80211_rfkill_poll; 399 + rfkill_resume_polling(drv->rfkill); 400 + } 401 + EXPORT_SYMBOL(wiphy_rfkill_start_polling); 402 + 403 + void wiphy_rfkill_stop_polling(struct wiphy *wiphy) 404 + { 405 + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); 406 + 407 + rfkill_pause_polling(drv->rfkill); 408 + } 409 + EXPORT_SYMBOL(wiphy_rfkill_stop_polling); 410 + 440 411 void wiphy_unregister(struct wiphy *wiphy) 441 412 { 442 413 struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); 414 + 415 + rfkill_unregister(drv->rfkill); 443 416 444 417 /* protect the device list */ 445 418 mutex_lock(&cfg80211_mutex); ··· 502 425 void cfg80211_dev_free(struct cfg80211_registered_device *drv) 503 426 { 504 427 struct cfg80211_internal_bss *scan, *tmp; 428 + rfkill_destroy(drv->rfkill); 505 429 mutex_destroy(&drv->mtx); 506 430 mutex_destroy(&drv->devlist_mtx); 507 431 list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) ··· 516 438 } 517 439 EXPORT_SYMBOL(wiphy_free); 518 440 441 + void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) 442 + { 443 + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); 444 + 445 + if (rfkill_set_hw_state(drv->rfkill, blocked)) 446 + schedule_work(&drv->rfkill_sync); 447 + } 448 + EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); 449 + 519 450 static int cfg80211_netdev_notifier_call(struct notifier_block * nb, 520 451 unsigned long state, 521 452 void *ndev) ··· 533 446 struct cfg80211_registered_device *rdev; 534 447 535 448 if (!dev->ieee80211_ptr) 536 - return 0; 449 + return NOTIFY_DONE; 537 450 538 451 rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); 539 452 ··· 579 492 } 580 493 mutex_unlock(&rdev->devlist_mtx); 581 494 break; 495 + case NETDEV_PRE_UP: 496 + if (rfkill_blocked(rdev->rfkill)) 497 + return notifier_from_errno(-ERFKILL); 498 + break; 582 499 } 583 500 584 - return 0; 501 + return NOTIFY_DONE; 585 502 } 586 503 587 504 static struct notifier_block cfg80211_netdev_notifier = {
+7
net/wireless/core.h
··· 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" ··· 25 23 * to avoid the deregister call to proceed while 26 24 * any call is in progress */ 27 25 struct mutex mtx; 26 + 27 + /* rfkill support */ 28 + struct rfkill_ops rfkill_ops; 29 + struct rfkill *rfkill; 30 + struct work_struct rfkill_sync; 28 31 29 32 /* ISO / IEC 3166 alpha2 for which this device is receiving 30 33 * country IEs on, this can help disregard country IEs from APs
+7 -4
net/wireless/wext-compat.c
··· 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 ··· 789 787 } 790 788 } 791 789 } else { 792 - type = TX_POWER_OFF; 790 + rfkill_set_sw_state(rdev->rfkill, true); 791 + schedule_work(&rdev->rfkill_sync); 792 + return 0; 793 793 } 794 794 795 795 return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);; ··· 815 811 return -EOPNOTSUPP; 816 812 817 813 err = rdev->ops->get_tx_power(wdev->wiphy, &val); 818 - /* HACK!!! */ 819 - if (err && err != -ENETDOWN) 814 + if (err) 820 815 return err; 821 816 822 817 /* well... oh well */ 823 818 data->txpower.fixed = 1; 824 - data->txpower.disabled = err == -ENETDOWN; 819 + data->txpower.disabled = rfkill_blocked(rdev->rfkill); 825 820 data->txpower.value = val; 826 821 data->txpower.flags = IW_TXPOW_DBM; 827 822