diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-08-27 06:35:58 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-08-27 13:53:31 -0400 |
commit | 34d4bc4d41d282a66dafe1b01a7d46bad468cefb (patch) | |
tree | ac0936b00f1ebd037be32fd0e5f304f26366e6c0 /net/mac80211/iface.c | |
parent | 87490f6db38999fee7f6d3dbecc5b94730c7e010 (diff) |
mac80211: support runtime interface type changes
Add support to mac80211 for changing the interface
type even when the interface is UP, if the driver
supports it.
To achieve this
* add a new driver callback for switching,
* split some of the interface up/down code out
into new functions (do_open/do_stop), and
* maintain an own __SDATA_RUNNING bit that will
not be set during interface type, so that any
other code doesn't use the interface.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r-- | net/mac80211/iface.c | 157 |
1 files changed, 124 insertions, 33 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index cba3d806d722..c1cc200ac81f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -148,7 +148,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, | |||
148 | return 0; | 148 | return 0; |
149 | } | 149 | } |
150 | 150 | ||
151 | static int ieee80211_open(struct net_device *dev) | 151 | /* |
152 | * NOTE: Be very careful when changing this function, it must NOT return | ||
153 | * an error on interface type changes that have been pre-checked, so most | ||
154 | * checks should be in ieee80211_check_concurrent_iface. | ||
155 | */ | ||
156 | static int ieee80211_do_open(struct net_device *dev, bool coming_up) | ||
152 | { | 157 | { |
153 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 158 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
154 | struct ieee80211_local *local = sdata->local; | 159 | struct ieee80211_local *local = sdata->local; |
@@ -157,15 +162,6 @@ static int ieee80211_open(struct net_device *dev) | |||
157 | int res; | 162 | int res; |
158 | u32 hw_reconf_flags = 0; | 163 | u32 hw_reconf_flags = 0; |
159 | 164 | ||
160 | /* fail early if user set an invalid address */ | ||
161 | if (!is_zero_ether_addr(dev->dev_addr) && | ||
162 | !is_valid_ether_addr(dev->dev_addr)) | ||
163 | return -EADDRNOTAVAIL; | ||
164 | |||
165 | res = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); | ||
166 | if (res) | ||
167 | return res; | ||
168 | |||
169 | switch (sdata->vif.type) { | 165 | switch (sdata->vif.type) { |
170 | case NL80211_IFTYPE_WDS: | 166 | case NL80211_IFTYPE_WDS: |
171 | if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) | 167 | if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) |
@@ -258,9 +254,11 @@ static int ieee80211_open(struct net_device *dev) | |||
258 | netif_carrier_on(dev); | 254 | netif_carrier_on(dev); |
259 | break; | 255 | break; |
260 | default: | 256 | default: |
261 | res = drv_add_interface(local, &sdata->vif); | 257 | if (coming_up) { |
262 | if (res) | 258 | res = drv_add_interface(local, &sdata->vif); |
263 | goto err_stop; | 259 | if (res) |
260 | goto err_stop; | ||
261 | } | ||
264 | 262 | ||
265 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | 263 | if (ieee80211_vif_is_mesh(&sdata->vif)) { |
266 | local->fif_other_bss++; | 264 | local->fif_other_bss++; |
@@ -316,7 +314,9 @@ static int ieee80211_open(struct net_device *dev) | |||
316 | hw_reconf_flags |= __ieee80211_recalc_idle(local); | 314 | hw_reconf_flags |= __ieee80211_recalc_idle(local); |
317 | mutex_unlock(&local->mtx); | 315 | mutex_unlock(&local->mtx); |
318 | 316 | ||
319 | local->open_count++; | 317 | if (coming_up) |
318 | local->open_count++; | ||
319 | |||
320 | if (hw_reconf_flags) { | 320 | if (hw_reconf_flags) { |
321 | ieee80211_hw_config(local, hw_reconf_flags); | 321 | ieee80211_hw_config(local, hw_reconf_flags); |
322 | /* | 322 | /* |
@@ -331,6 +331,8 @@ static int ieee80211_open(struct net_device *dev) | |||
331 | 331 | ||
332 | netif_tx_start_all_queues(dev); | 332 | netif_tx_start_all_queues(dev); |
333 | 333 | ||
334 | set_bit(SDATA_STATE_RUNNING, &sdata->state); | ||
335 | |||
334 | return 0; | 336 | return 0; |
335 | err_del_interface: | 337 | err_del_interface: |
336 | drv_remove_interface(local, &sdata->vif); | 338 | drv_remove_interface(local, &sdata->vif); |
@@ -344,19 +346,38 @@ static int ieee80211_open(struct net_device *dev) | |||
344 | return res; | 346 | return res; |
345 | } | 347 | } |
346 | 348 | ||
347 | static int ieee80211_stop(struct net_device *dev) | 349 | static int ieee80211_open(struct net_device *dev) |
348 | { | 350 | { |
349 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 351 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
352 | int err; | ||
353 | |||
354 | /* fail early if user set an invalid address */ | ||
355 | if (!is_zero_ether_addr(dev->dev_addr) && | ||
356 | !is_valid_ether_addr(dev->dev_addr)) | ||
357 | return -EADDRNOTAVAIL; | ||
358 | |||
359 | err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); | ||
360 | if (err) | ||
361 | return err; | ||
362 | |||
363 | return ieee80211_do_open(dev, true); | ||
364 | } | ||
365 | |||
366 | static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | ||
367 | bool going_down) | ||
368 | { | ||
350 | struct ieee80211_local *local = sdata->local; | 369 | struct ieee80211_local *local = sdata->local; |
351 | unsigned long flags; | 370 | unsigned long flags; |
352 | struct sk_buff *skb, *tmp; | 371 | struct sk_buff *skb, *tmp; |
353 | u32 hw_reconf_flags = 0; | 372 | u32 hw_reconf_flags = 0; |
354 | int i; | 373 | int i; |
355 | 374 | ||
375 | clear_bit(SDATA_STATE_RUNNING, &sdata->state); | ||
376 | |||
356 | /* | 377 | /* |
357 | * Stop TX on this interface first. | 378 | * Stop TX on this interface first. |
358 | */ | 379 | */ |
359 | netif_tx_stop_all_queues(dev); | 380 | netif_tx_stop_all_queues(sdata->dev); |
360 | 381 | ||
361 | /* | 382 | /* |
362 | * Purge work for this interface. | 383 | * Purge work for this interface. |
@@ -394,11 +415,12 @@ static int ieee80211_stop(struct net_device *dev) | |||
394 | if (sdata->vif.type == NL80211_IFTYPE_AP) | 415 | if (sdata->vif.type == NL80211_IFTYPE_AP) |
395 | local->fif_pspoll--; | 416 | local->fif_pspoll--; |
396 | 417 | ||
397 | netif_addr_lock_bh(dev); | 418 | netif_addr_lock_bh(sdata->dev); |
398 | spin_lock_bh(&local->filter_lock); | 419 | spin_lock_bh(&local->filter_lock); |
399 | __hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len); | 420 | __hw_addr_unsync(&local->mc_list, &sdata->dev->mc, |
421 | sdata->dev->addr_len); | ||
400 | spin_unlock_bh(&local->filter_lock); | 422 | spin_unlock_bh(&local->filter_lock); |
401 | netif_addr_unlock_bh(dev); | 423 | netif_addr_unlock_bh(sdata->dev); |
402 | 424 | ||
403 | ieee80211_configure_filter(local); | 425 | ieee80211_configure_filter(local); |
404 | 426 | ||
@@ -432,7 +454,8 @@ static int ieee80211_stop(struct net_device *dev) | |||
432 | WARN_ON(!list_empty(&sdata->u.ap.vlans)); | 454 | WARN_ON(!list_empty(&sdata->u.ap.vlans)); |
433 | } | 455 | } |
434 | 456 | ||
435 | local->open_count--; | 457 | if (going_down) |
458 | local->open_count--; | ||
436 | 459 | ||
437 | switch (sdata->vif.type) { | 460 | switch (sdata->vif.type) { |
438 | case NL80211_IFTYPE_AP_VLAN: | 461 | case NL80211_IFTYPE_AP_VLAN: |
@@ -504,7 +527,8 @@ static int ieee80211_stop(struct net_device *dev) | |||
504 | */ | 527 | */ |
505 | ieee80211_free_keys(sdata); | 528 | ieee80211_free_keys(sdata); |
506 | 529 | ||
507 | drv_remove_interface(local, &sdata->vif); | 530 | if (going_down) |
531 | drv_remove_interface(local, &sdata->vif); | ||
508 | } | 532 | } |
509 | 533 | ||
510 | sdata->bss = NULL; | 534 | sdata->bss = NULL; |
@@ -540,6 +564,13 @@ static int ieee80211_stop(struct net_device *dev) | |||
540 | } | 564 | } |
541 | } | 565 | } |
542 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 566 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
567 | } | ||
568 | |||
569 | static int ieee80211_stop(struct net_device *dev) | ||
570 | { | ||
571 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
572 | |||
573 | ieee80211_do_stop(sdata, true); | ||
543 | 574 | ||
544 | return 0; | 575 | return 0; |
545 | } | 576 | } |
@@ -857,9 +888,72 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
857 | ieee80211_debugfs_add_netdev(sdata); | 888 | ieee80211_debugfs_add_netdev(sdata); |
858 | } | 889 | } |
859 | 890 | ||
891 | static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, | ||
892 | enum nl80211_iftype type) | ||
893 | { | ||
894 | struct ieee80211_local *local = sdata->local; | ||
895 | int ret, err; | ||
896 | |||
897 | ASSERT_RTNL(); | ||
898 | |||
899 | if (!local->ops->change_interface) | ||
900 | return -EBUSY; | ||
901 | |||
902 | switch (sdata->vif.type) { | ||
903 | case NL80211_IFTYPE_AP: | ||
904 | case NL80211_IFTYPE_STATION: | ||
905 | case NL80211_IFTYPE_ADHOC: | ||
906 | /* | ||
907 | * Could maybe also all others here? | ||
908 | * Just not sure how that interacts | ||
909 | * with the RX/config path e.g. for | ||
910 | * mesh. | ||
911 | */ | ||
912 | break; | ||
913 | default: | ||
914 | return -EBUSY; | ||
915 | } | ||
916 | |||
917 | switch (type) { | ||
918 | case NL80211_IFTYPE_AP: | ||
919 | case NL80211_IFTYPE_STATION: | ||
920 | case NL80211_IFTYPE_ADHOC: | ||
921 | /* | ||
922 | * Could probably support everything | ||
923 | * but WDS here (WDS do_open can fail | ||
924 | * under memory pressure, which this | ||
925 | * code isn't prepared to handle). | ||
926 | */ | ||
927 | break; | ||
928 | default: | ||
929 | return -EBUSY; | ||
930 | } | ||
931 | |||
932 | ret = ieee80211_check_concurrent_iface(sdata, type); | ||
933 | if (ret) | ||
934 | return ret; | ||
935 | |||
936 | ieee80211_do_stop(sdata, false); | ||
937 | |||
938 | ieee80211_teardown_sdata(sdata->dev); | ||
939 | |||
940 | ret = drv_change_interface(local, sdata, type); | ||
941 | if (ret) | ||
942 | type = sdata->vif.type; | ||
943 | |||
944 | ieee80211_setup_sdata(sdata, type); | ||
945 | |||
946 | err = ieee80211_do_open(sdata->dev, false); | ||
947 | WARN(err, "type change: do_open returned %d", err); | ||
948 | |||
949 | return ret; | ||
950 | } | ||
951 | |||
860 | int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, | 952 | int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, |
861 | enum nl80211_iftype type) | 953 | enum nl80211_iftype type) |
862 | { | 954 | { |
955 | int ret; | ||
956 | |||
863 | ASSERT_RTNL(); | 957 | ASSERT_RTNL(); |
864 | 958 | ||
865 | if (type == sdata->vif.type) | 959 | if (type == sdata->vif.type) |
@@ -870,18 +964,15 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, | |||
870 | type == NL80211_IFTYPE_ADHOC) | 964 | type == NL80211_IFTYPE_ADHOC) |
871 | return -EOPNOTSUPP; | 965 | return -EOPNOTSUPP; |
872 | 966 | ||
873 | /* | 967 | if (ieee80211_sdata_running(sdata)) { |
874 | * We could, here, on changes between IBSS/STA/MESH modes, | 968 | ret = ieee80211_runtime_change_iftype(sdata, type); |
875 | * invoke an MLME function instead that disassociates etc. | 969 | if (ret) |
876 | * and goes into the requested mode. | 970 | return ret; |
877 | */ | 971 | } else { |
878 | 972 | /* Purge and reset type-dependent state. */ | |
879 | if (ieee80211_sdata_running(sdata)) | 973 | ieee80211_teardown_sdata(sdata->dev); |
880 | return -EBUSY; | 974 | ieee80211_setup_sdata(sdata, type); |
881 | 975 | } | |
882 | /* Purge and reset type-dependent state. */ | ||
883 | ieee80211_teardown_sdata(sdata->dev); | ||
884 | ieee80211_setup_sdata(sdata, type); | ||
885 | 976 | ||
886 | /* reset some values that shouldn't be kept across type changes */ | 977 | /* reset some values that shouldn't be kept across type changes */ |
887 | sdata->vif.bss_conf.basic_rates = | 978 | sdata->vif.bss_conf.basic_rates = |