diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-06-02 07:01:41 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-06-03 14:06:14 -0400 |
commit | 1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3 (patch) | |
tree | 642882153a48e910a415e6bb23bcfb79fadef6dd /net | |
parent | 6081162e2ed78dfcf149b076b047078ab1445cc2 (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 20 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 7 | ||||
-rw-r--r-- | net/mac80211/iface.c | 4 | ||||
-rw-r--r-- | net/mac80211/util.c | 2 | ||||
-rw-r--r-- | net/wireless/Kconfig | 3 | ||||
-rw-r--r-- | net/wireless/core.c | 97 | ||||
-rw-r--r-- | net/wireless/core.h | 7 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 11 |
8 files changed, 128 insertions, 23 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 81258acf48bc..a9211cc183cb 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1340,7 +1340,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, | |||
1340 | struct ieee80211_local *local = wiphy_priv(wiphy); | 1340 | struct ieee80211_local *local = wiphy_priv(wiphy); |
1341 | struct ieee80211_channel *chan = local->hw.conf.channel; | 1341 | struct ieee80211_channel *chan = local->hw.conf.channel; |
1342 | u32 changes = 0; | 1342 | u32 changes = 0; |
1343 | bool radio_enabled = true; | ||
1344 | 1343 | ||
1345 | switch (type) { | 1344 | switch (type) { |
1346 | case TX_POWER_AUTOMATIC: | 1345 | case TX_POWER_AUTOMATIC: |
@@ -1359,14 +1358,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, | |||
1359 | return -EINVAL; | 1358 | return -EINVAL; |
1360 | local->user_power_level = dbm; | 1359 | local->user_power_level = dbm; |
1361 | break; | 1360 | break; |
1362 | case TX_POWER_OFF: | ||
1363 | radio_enabled = false; | ||
1364 | break; | ||
1365 | } | ||
1366 | |||
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 | } | 1361 | } |
1371 | 1362 | ||
1372 | ieee80211_hw_config(local, changes); | 1363 | ieee80211_hw_config(local, changes); |
@@ -1380,12 +1371,16 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) | |||
1380 | 1371 | ||
1381 | *dbm = local->hw.conf.power_level; | 1372 | *dbm = local->hw.conf.power_level; |
1382 | 1373 | ||
1383 | if (!local->hw.conf.radio_enabled) | ||
1384 | return -ENETDOWN; | ||
1385 | |||
1386 | return 0; | 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 | struct cfg80211_ops mac80211_config_ops = { | 1384 | struct cfg80211_ops mac80211_config_ops = { |
1390 | .add_virtual_intf = ieee80211_add_iface, | 1385 | .add_virtual_intf = ieee80211_add_iface, |
1391 | .del_virtual_intf = ieee80211_del_iface, | 1386 | .del_virtual_intf = ieee80211_del_iface, |
@@ -1427,4 +1422,5 @@ struct cfg80211_ops mac80211_config_ops = { | |||
1427 | .set_wiphy_params = ieee80211_set_wiphy_params, | 1422 | .set_wiphy_params = ieee80211_set_wiphy_params, |
1428 | .set_tx_power = ieee80211_set_tx_power, | 1423 | .set_tx_power = ieee80211_set_tx_power, |
1429 | .get_tx_power = ieee80211_get_tx_power, | 1424 | .get_tx_power = ieee80211_get_tx_power, |
1425 | .rfkill_poll = ieee80211_rfkill_poll, | ||
1430 | }; | 1426 | }; |
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 3912b5334b9c..b13446afd48f 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -181,4 +181,11 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, | |||
181 | sta, tid, ssn); | 181 | sta, tid, ssn); |
182 | return -EOPNOTSUPP; | 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 | #endif /* __MAC80211_DRIVER_OPS */ | 191 | #endif /* __MAC80211_DRIVER_OPS */ |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8c9f1c722cdb..b7c8a4484298 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -170,7 +170,7 @@ static int ieee80211_open(struct net_device *dev) | |||
170 | goto err_del_bss; | 170 | goto err_del_bss; |
171 | /* we're brought up, everything changes */ | 171 | /* we're brought up, everything changes */ |
172 | hw_reconf_flags = ~0; | 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 @@ static int ieee80211_stop(struct net_device *dev) | |||
560 | 560 | ||
561 | drv_stop(local); | 561 | drv_stop(local); |
562 | 562 | ||
563 | ieee80211_led_radio(local, 0); | 563 | ieee80211_led_radio(local, false); |
564 | 564 | ||
565 | flush_workqueue(local->hw.workqueue); | 565 | flush_workqueue(local->hw.workqueue); |
566 | 566 | ||
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 31284c984e38..22f63815fb36 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -973,7 +973,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
973 | if (local->open_count) { | 973 | if (local->open_count) { |
974 | res = drv_start(local); | 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 | /* add interfaces */ | 979 | /* add interfaces */ |
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 45005497c634..4428dd5e911d 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig | |||
@@ -1,5 +1,6 @@ | |||
1 | config CFG80211 | 1 | config CFG80211 |
2 | tristate "Improved wireless configuration API" | 2 | tristate "Improved wireless configuration API" |
3 | depends on RFKILL || !RFKILL | ||
3 | 4 | ||
4 | config CFG80211_REG_DEBUG | 5 | config CFG80211_REG_DEBUG |
5 | bool "cfg80211 regulatory debugging" | 6 | bool "cfg80211 regulatory debugging" |
diff --git a/net/wireless/core.c b/net/wireless/core.c index a5dbea1da476..3b74b88e10a3 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/debugfs.h> | 12 | #include <linux/debugfs.h> |
13 | #include <linux/notifier.h> | 13 | #include <linux/notifier.h> |
14 | #include <linux/device.h> | 14 | #include <linux/device.h> |
15 | #include <linux/rtnetlink.h> | ||
15 | #include <net/genetlink.h> | 16 | #include <net/genetlink.h> |
16 | #include <net/cfg80211.h> | 17 | #include <net/cfg80211.h> |
17 | #include "nl80211.h" | 18 | #include "nl80211.h" |
@@ -227,6 +228,41 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, | |||
227 | return 0; | 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 | /* exported functions */ | 266 | /* exported functions */ |
231 | 267 | ||
232 | struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | 268 | struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) |
@@ -274,6 +310,18 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
274 | drv->wiphy.dev.class = &ieee80211_class; | 310 | drv->wiphy.dev.class = &ieee80211_class; |
275 | drv->wiphy.dev.platform_data = drv; | 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 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. | 326 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. |
279 | * Fragmentation and RTS threshold are disabled by default with the | 327 | * Fragmentation and RTS threshold are disabled by default with the |
@@ -356,6 +404,10 @@ int wiphy_register(struct wiphy *wiphy) | |||
356 | if (res) | 404 | if (res) |
357 | goto out_unlock; | 405 | goto out_unlock; |
358 | 406 | ||
407 | res = rfkill_register(drv->rfkill); | ||
408 | if (res) | ||
409 | goto out_rm_dev; | ||
410 | |||
359 | list_add(&drv->list, &cfg80211_drv_list); | 411 | list_add(&drv->list, &cfg80211_drv_list); |
360 | 412 | ||
361 | /* add to debugfs */ | 413 | /* add to debugfs */ |
@@ -379,16 +431,41 @@ int wiphy_register(struct wiphy *wiphy) | |||
379 | cfg80211_debugfs_drv_add(drv); | 431 | cfg80211_debugfs_drv_add(drv); |
380 | 432 | ||
381 | res = 0; | 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 | mutex_unlock(&cfg80211_mutex); | 439 | mutex_unlock(&cfg80211_mutex); |
384 | return res; | 440 | return res; |
385 | } | 441 | } |
386 | EXPORT_SYMBOL(wiphy_register); | 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 | void wiphy_unregister(struct wiphy *wiphy) | 463 | void wiphy_unregister(struct wiphy *wiphy) |
389 | { | 464 | { |
390 | struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); | 465 | struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); |
391 | 466 | ||
467 | rfkill_unregister(drv->rfkill); | ||
468 | |||
392 | /* protect the device list */ | 469 | /* protect the device list */ |
393 | mutex_lock(&cfg80211_mutex); | 470 | mutex_lock(&cfg80211_mutex); |
394 | 471 | ||
@@ -425,6 +502,7 @@ EXPORT_SYMBOL(wiphy_unregister); | |||
425 | void cfg80211_dev_free(struct cfg80211_registered_device *drv) | 502 | void cfg80211_dev_free(struct cfg80211_registered_device *drv) |
426 | { | 503 | { |
427 | struct cfg80211_internal_bss *scan, *tmp; | 504 | struct cfg80211_internal_bss *scan, *tmp; |
505 | rfkill_destroy(drv->rfkill); | ||
428 | mutex_destroy(&drv->mtx); | 506 | mutex_destroy(&drv->mtx); |
429 | mutex_destroy(&drv->devlist_mtx); | 507 | mutex_destroy(&drv->devlist_mtx); |
430 | list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) | 508 | list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) |
@@ -438,6 +516,15 @@ void wiphy_free(struct wiphy *wiphy) | |||
438 | } | 516 | } |
439 | EXPORT_SYMBOL(wiphy_free); | 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 | static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | 528 | static int cfg80211_netdev_notifier_call(struct notifier_block * nb, |
442 | unsigned long state, | 529 | unsigned long state, |
443 | void *ndev) | 530 | void *ndev) |
@@ -446,7 +533,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
446 | struct cfg80211_registered_device *rdev; | 533 | struct cfg80211_registered_device *rdev; |
447 | 534 | ||
448 | if (!dev->ieee80211_ptr) | 535 | if (!dev->ieee80211_ptr) |
449 | return 0; | 536 | return NOTIFY_DONE; |
450 | 537 | ||
451 | rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); | 538 | rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); |
452 | 539 | ||
@@ -492,9 +579,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
492 | } | 579 | } |
493 | mutex_unlock(&rdev->devlist_mtx); | 580 | mutex_unlock(&rdev->devlist_mtx); |
494 | break; | 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 | static struct notifier_block cfg80211_netdev_notifier = { | 591 | static struct notifier_block cfg80211_netdev_notifier = { |
diff --git a/net/wireless/core.h b/net/wireless/core.h index ab512bcd8153..bfa340c7abb5 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -11,6 +11,8 @@ | |||
11 | #include <linux/kref.h> | 11 | #include <linux/kref.h> |
12 | #include <linux/rbtree.h> | 12 | #include <linux/rbtree.h> |
13 | #include <linux/debugfs.h> | 13 | #include <linux/debugfs.h> |
14 | #include <linux/rfkill.h> | ||
15 | #include <linux/workqueue.h> | ||
14 | #include <net/genetlink.h> | 16 | #include <net/genetlink.h> |
15 | #include <net/cfg80211.h> | 17 | #include <net/cfg80211.h> |
16 | #include "reg.h" | 18 | #include "reg.h" |
@@ -24,6 +26,11 @@ struct cfg80211_registered_device { | |||
24 | * any call is in progress */ | 26 | * any call is in progress */ |
25 | struct mutex mtx; | 27 | struct mutex mtx; |
26 | 28 | ||
29 | /* rfkill support */ | ||
30 | struct rfkill_ops rfkill_ops; | ||
31 | struct rfkill *rfkill; | ||
32 | struct work_struct rfkill_sync; | ||
33 | |||
27 | /* ISO / IEC 3166 alpha2 for which this device is receiving | 34 | /* ISO / IEC 3166 alpha2 for which this device is receiving |
28 | * country IEs on, this can help disregard country IEs from APs | 35 | * country IEs on, this can help disregard country IEs from APs |
29 | * on the same alpha2 quickly. The alpha2 may differ from | 36 | * on the same alpha2 quickly. The alpha2 may differ from |
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 9fbfb8536e75..d030c5315672 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -764,6 +764,8 @@ int cfg80211_wext_siwtxpower(struct net_device *dev, | |||
764 | 764 | ||
765 | /* only change when not disabling */ | 765 | /* only change when not disabling */ |
766 | if (!data->txpower.disabled) { | 766 | if (!data->txpower.disabled) { |
767 | rfkill_set_sw_state(rdev->rfkill, false); | ||
768 | |||
767 | if (data->txpower.fixed) { | 769 | if (data->txpower.fixed) { |
768 | /* | 770 | /* |
769 | * wext doesn't support negative values, see | 771 | * wext doesn't support negative values, see |
@@ -787,7 +789,9 @@ int cfg80211_wext_siwtxpower(struct net_device *dev, | |||
787 | } | 789 | } |
788 | } | 790 | } |
789 | } else { | 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 | return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);; | 797 | return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);; |
@@ -811,13 +815,12 @@ int cfg80211_wext_giwtxpower(struct net_device *dev, | |||
811 | return -EOPNOTSUPP; | 815 | return -EOPNOTSUPP; |
812 | 816 | ||
813 | err = rdev->ops->get_tx_power(wdev->wiphy, &val); | 817 | err = rdev->ops->get_tx_power(wdev->wiphy, &val); |
814 | /* HACK!!! */ | 818 | if (err) |
815 | if (err && err != -ENETDOWN) | ||
816 | return err; | 819 | return err; |
817 | 820 | ||
818 | /* well... oh well */ | 821 | /* well... oh well */ |
819 | data->txpower.fixed = 1; | 822 | data->txpower.fixed = 1; |
820 | data->txpower.disabled = err == -ENETDOWN; | 823 | data->txpower.disabled = rfkill_blocked(rdev->rfkill); |
821 | data->txpower.value = val; | 824 | data->txpower.value = val; |
822 | data->txpower.flags = IW_TXPOW_DBM; | 825 | data->txpower.flags = IW_TXPOW_DBM; |
823 | 826 | ||