diff options
Diffstat (limited to 'net/mac80211/ibss.c')
-rw-r--r-- | net/mac80211/ibss.c | 171 |
1 files changed, 86 insertions, 85 deletions
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b2cc1fda6cfd..c691780725a7 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -43,6 +43,8 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, | |||
43 | { | 43 | { |
44 | u16 auth_alg, auth_transaction, status_code; | 44 | u16 auth_alg, auth_transaction, status_code; |
45 | 45 | ||
46 | lockdep_assert_held(&sdata->u.ibss.mtx); | ||
47 | |||
46 | if (len < 24 + 6) | 48 | if (len < 24 + 6) |
47 | return; | 49 | return; |
48 | 50 | ||
@@ -78,6 +80,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
78 | u32 bss_change; | 80 | u32 bss_change; |
79 | u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; | 81 | u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; |
80 | 82 | ||
83 | lockdep_assert_held(&ifibss->mtx); | ||
84 | |||
81 | /* Reset own TSF to allow time synchronization work. */ | 85 | /* Reset own TSF to allow time synchronization work. */ |
82 | drv_reset_tsf(local); | 86 | drv_reset_tsf(local); |
83 | 87 | ||
@@ -172,11 +176,13 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
172 | rcu_assign_pointer(ifibss->presp, skb); | 176 | rcu_assign_pointer(ifibss->presp, skb); |
173 | 177 | ||
174 | sdata->vif.bss_conf.beacon_int = beacon_int; | 178 | sdata->vif.bss_conf.beacon_int = beacon_int; |
179 | sdata->vif.bss_conf.basic_rates = basic_rates; | ||
175 | bss_change = BSS_CHANGED_BEACON_INT; | 180 | bss_change = BSS_CHANGED_BEACON_INT; |
176 | bss_change |= ieee80211_reset_erp_info(sdata); | 181 | bss_change |= ieee80211_reset_erp_info(sdata); |
177 | bss_change |= BSS_CHANGED_BSSID; | 182 | bss_change |= BSS_CHANGED_BSSID; |
178 | bss_change |= BSS_CHANGED_BEACON; | 183 | bss_change |= BSS_CHANGED_BEACON; |
179 | bss_change |= BSS_CHANGED_BEACON_ENABLED; | 184 | bss_change |= BSS_CHANGED_BEACON_ENABLED; |
185 | bss_change |= BSS_CHANGED_BASIC_RATES; | ||
180 | bss_change |= BSS_CHANGED_IBSS; | 186 | bss_change |= BSS_CHANGED_IBSS; |
181 | sdata->vif.bss_conf.ibss_joined = true; | 187 | sdata->vif.bss_conf.ibss_joined = true; |
182 | ieee80211_bss_info_change_notify(sdata, bss_change); | 188 | ieee80211_bss_info_change_notify(sdata, bss_change); |
@@ -203,6 +209,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
203 | int i, j; | 209 | int i, j; |
204 | u16 beacon_int = cbss->beacon_interval; | 210 | u16 beacon_int = cbss->beacon_interval; |
205 | 211 | ||
212 | lockdep_assert_held(&sdata->u.ibss.mtx); | ||
213 | |||
206 | if (beacon_int < 10) | 214 | if (beacon_int < 10) |
207 | beacon_int = 10; | 215 | beacon_int = 10; |
208 | 216 | ||
@@ -447,6 +455,8 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) | |||
447 | int active = 0; | 455 | int active = 0; |
448 | struct sta_info *sta; | 456 | struct sta_info *sta; |
449 | 457 | ||
458 | lockdep_assert_held(&sdata->u.ibss.mtx); | ||
459 | |||
450 | rcu_read_lock(); | 460 | rcu_read_lock(); |
451 | 461 | ||
452 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | 462 | list_for_each_entry_rcu(sta, &local->sta_list, list) { |
@@ -471,6 +481,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) | |||
471 | { | 481 | { |
472 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 482 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
473 | 483 | ||
484 | lockdep_assert_held(&ifibss->mtx); | ||
485 | |||
474 | mod_timer(&ifibss->timer, | 486 | mod_timer(&ifibss->timer, |
475 | round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); | 487 | round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); |
476 | 488 | ||
@@ -503,6 +515,8 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) | |||
503 | u16 capability; | 515 | u16 capability; |
504 | int i; | 516 | int i; |
505 | 517 | ||
518 | lockdep_assert_held(&ifibss->mtx); | ||
519 | |||
506 | if (ifibss->fixed_bssid) { | 520 | if (ifibss->fixed_bssid) { |
507 | memcpy(bssid, ifibss->bssid, ETH_ALEN); | 521 | memcpy(bssid, ifibss->bssid, ETH_ALEN); |
508 | } else { | 522 | } else { |
@@ -529,7 +543,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) | |||
529 | sdata->drop_unencrypted = 0; | 543 | sdata->drop_unencrypted = 0; |
530 | 544 | ||
531 | __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, | 545 | __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, |
532 | ifibss->channel, 3, /* first two are basic */ | 546 | ifibss->channel, ifibss->basic_rates, |
533 | capability, 0); | 547 | capability, 0); |
534 | } | 548 | } |
535 | 549 | ||
@@ -547,6 +561,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) | |||
547 | int active_ibss; | 561 | int active_ibss; |
548 | u16 capability; | 562 | u16 capability; |
549 | 563 | ||
564 | lockdep_assert_held(&ifibss->mtx); | ||
565 | |||
550 | active_ibss = ieee80211_sta_active_ibss(sdata); | 566 | active_ibss = ieee80211_sta_active_ibss(sdata); |
551 | #ifdef CONFIG_MAC80211_IBSS_DEBUG | 567 | #ifdef CONFIG_MAC80211_IBSS_DEBUG |
552 | printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", | 568 | printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", |
@@ -635,6 +651,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, | |||
635 | struct ieee80211_mgmt *resp; | 651 | struct ieee80211_mgmt *resp; |
636 | u8 *pos, *end; | 652 | u8 *pos, *end; |
637 | 653 | ||
654 | lockdep_assert_held(&ifibss->mtx); | ||
655 | |||
638 | if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || | 656 | if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || |
639 | len < 24 + 2 || !ifibss->presp) | 657 | len < 24 + 2 || !ifibss->presp) |
640 | return; | 658 | return; |
@@ -727,8 +745,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
727 | ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); | 745 | ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); |
728 | } | 746 | } |
729 | 747 | ||
730 | static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | 748 | void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, |
731 | struct sk_buff *skb) | 749 | struct sk_buff *skb) |
732 | { | 750 | { |
733 | struct ieee80211_rx_status *rx_status; | 751 | struct ieee80211_rx_status *rx_status; |
734 | struct ieee80211_mgmt *mgmt; | 752 | struct ieee80211_mgmt *mgmt; |
@@ -738,6 +756,8 @@ static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
738 | mgmt = (struct ieee80211_mgmt *) skb->data; | 756 | mgmt = (struct ieee80211_mgmt *) skb->data; |
739 | fc = le16_to_cpu(mgmt->frame_control); | 757 | fc = le16_to_cpu(mgmt->frame_control); |
740 | 758 | ||
759 | mutex_lock(&sdata->u.ibss.mtx); | ||
760 | |||
741 | switch (fc & IEEE80211_FCTL_STYPE) { | 761 | switch (fc & IEEE80211_FCTL_STYPE) { |
742 | case IEEE80211_STYPE_PROBE_REQ: | 762 | case IEEE80211_STYPE_PROBE_REQ: |
743 | ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len); | 763 | ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len); |
@@ -755,35 +775,22 @@ static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
755 | break; | 775 | break; |
756 | } | 776 | } |
757 | 777 | ||
758 | kfree_skb(skb); | 778 | mutex_unlock(&sdata->u.ibss.mtx); |
759 | } | 779 | } |
760 | 780 | ||
761 | static void ieee80211_ibss_work(struct work_struct *work) | 781 | void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) |
762 | { | 782 | { |
763 | struct ieee80211_sub_if_data *sdata = | 783 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
764 | container_of(work, struct ieee80211_sub_if_data, u.ibss.work); | ||
765 | struct ieee80211_local *local = sdata->local; | ||
766 | struct ieee80211_if_ibss *ifibss; | ||
767 | struct sk_buff *skb; | ||
768 | |||
769 | if (WARN_ON(local->suspended)) | ||
770 | return; | ||
771 | |||
772 | if (!ieee80211_sdata_running(sdata)) | ||
773 | return; | ||
774 | |||
775 | if (local->scanning) | ||
776 | return; | ||
777 | |||
778 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_ADHOC)) | ||
779 | return; | ||
780 | ifibss = &sdata->u.ibss; | ||
781 | 784 | ||
782 | while ((skb = skb_dequeue(&ifibss->skb_queue))) | 785 | mutex_lock(&ifibss->mtx); |
783 | ieee80211_ibss_rx_queued_mgmt(sdata, skb); | ||
784 | 786 | ||
785 | if (!test_and_clear_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request)) | 787 | /* |
786 | return; | 788 | * Work could be scheduled after scan or similar |
789 | * when we aren't even joined (or trying) with a | ||
790 | * network. | ||
791 | */ | ||
792 | if (!ifibss->ssid_len) | ||
793 | goto out; | ||
787 | 794 | ||
788 | switch (ifibss->state) { | 795 | switch (ifibss->state) { |
789 | case IEEE80211_IBSS_MLME_SEARCH: | 796 | case IEEE80211_IBSS_MLME_SEARCH: |
@@ -796,6 +803,9 @@ static void ieee80211_ibss_work(struct work_struct *work) | |||
796 | WARN_ON(1); | 803 | WARN_ON(1); |
797 | break; | 804 | break; |
798 | } | 805 | } |
806 | |||
807 | out: | ||
808 | mutex_unlock(&ifibss->mtx); | ||
799 | } | 809 | } |
800 | 810 | ||
801 | static void ieee80211_ibss_timer(unsigned long data) | 811 | static void ieee80211_ibss_timer(unsigned long data) |
@@ -810,8 +820,7 @@ static void ieee80211_ibss_timer(unsigned long data) | |||
810 | return; | 820 | return; |
811 | } | 821 | } |
812 | 822 | ||
813 | set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request); | 823 | ieee80211_queue_work(&local->hw, &sdata->work); |
814 | ieee80211_queue_work(&local->hw, &ifibss->work); | ||
815 | } | 824 | } |
816 | 825 | ||
817 | #ifdef CONFIG_PM | 826 | #ifdef CONFIG_PM |
@@ -819,7 +828,6 @@ void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata) | |||
819 | { | 828 | { |
820 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 829 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
821 | 830 | ||
822 | cancel_work_sync(&ifibss->work); | ||
823 | if (del_timer_sync(&ifibss->timer)) | 831 | if (del_timer_sync(&ifibss->timer)) |
824 | ifibss->timer_running = true; | 832 | ifibss->timer_running = true; |
825 | } | 833 | } |
@@ -839,10 +847,9 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
839 | { | 847 | { |
840 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 848 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
841 | 849 | ||
842 | INIT_WORK(&ifibss->work, ieee80211_ibss_work); | ||
843 | setup_timer(&ifibss->timer, ieee80211_ibss_timer, | 850 | setup_timer(&ifibss->timer, ieee80211_ibss_timer, |
844 | (unsigned long) sdata); | 851 | (unsigned long) sdata); |
845 | skb_queue_head_init(&ifibss->skb_queue); | 852 | mutex_init(&ifibss->mtx); |
846 | } | 853 | } |
847 | 854 | ||
848 | /* scan finished notification */ | 855 | /* scan finished notification */ |
@@ -856,45 +863,28 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) | |||
856 | continue; | 863 | continue; |
857 | if (sdata->vif.type != NL80211_IFTYPE_ADHOC) | 864 | if (sdata->vif.type != NL80211_IFTYPE_ADHOC) |
858 | continue; | 865 | continue; |
859 | if (!sdata->u.ibss.ssid_len) | ||
860 | continue; | ||
861 | sdata->u.ibss.last_scan_completed = jiffies; | 866 | sdata->u.ibss.last_scan_completed = jiffies; |
862 | mod_timer(&sdata->u.ibss.timer, 0); | 867 | ieee80211_queue_work(&local->hw, &sdata->work); |
863 | } | 868 | } |
864 | mutex_unlock(&local->iflist_mtx); | 869 | mutex_unlock(&local->iflist_mtx); |
865 | } | 870 | } |
866 | 871 | ||
867 | ieee80211_rx_result | ||
868 | ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | ||
869 | { | ||
870 | struct ieee80211_local *local = sdata->local; | ||
871 | struct ieee80211_mgmt *mgmt; | ||
872 | u16 fc; | ||
873 | |||
874 | if (skb->len < 24) | ||
875 | return RX_DROP_MONITOR; | ||
876 | |||
877 | mgmt = (struct ieee80211_mgmt *) skb->data; | ||
878 | fc = le16_to_cpu(mgmt->frame_control); | ||
879 | |||
880 | switch (fc & IEEE80211_FCTL_STYPE) { | ||
881 | case IEEE80211_STYPE_PROBE_RESP: | ||
882 | case IEEE80211_STYPE_BEACON: | ||
883 | case IEEE80211_STYPE_PROBE_REQ: | ||
884 | case IEEE80211_STYPE_AUTH: | ||
885 | skb_queue_tail(&sdata->u.ibss.skb_queue, skb); | ||
886 | ieee80211_queue_work(&local->hw, &sdata->u.ibss.work); | ||
887 | return RX_QUEUED; | ||
888 | } | ||
889 | |||
890 | return RX_DROP_MONITOR; | ||
891 | } | ||
892 | |||
893 | int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | 872 | int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, |
894 | struct cfg80211_ibss_params *params) | 873 | struct cfg80211_ibss_params *params) |
895 | { | 874 | { |
896 | struct sk_buff *skb; | 875 | struct sk_buff *skb; |
897 | 876 | ||
877 | skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + | ||
878 | 36 /* bitrates */ + | ||
879 | 34 /* SSID */ + | ||
880 | 3 /* DS params */ + | ||
881 | 4 /* IBSS params */ + | ||
882 | params->ie_len); | ||
883 | if (!skb) | ||
884 | return -ENOMEM; | ||
885 | |||
886 | mutex_lock(&sdata->u.ibss.mtx); | ||
887 | |||
898 | if (params->bssid) { | 888 | if (params->bssid) { |
899 | memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); | 889 | memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); |
900 | sdata->u.ibss.fixed_bssid = true; | 890 | sdata->u.ibss.fixed_bssid = true; |
@@ -902,6 +892,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
902 | sdata->u.ibss.fixed_bssid = false; | 892 | sdata->u.ibss.fixed_bssid = false; |
903 | 893 | ||
904 | sdata->u.ibss.privacy = params->privacy; | 894 | sdata->u.ibss.privacy = params->privacy; |
895 | sdata->u.ibss.basic_rates = params->basic_rates; | ||
905 | 896 | ||
906 | sdata->vif.bss_conf.beacon_int = params->beacon_interval; | 897 | sdata->vif.bss_conf.beacon_int = params->beacon_interval; |
907 | 898 | ||
@@ -922,34 +913,18 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
922 | sdata->u.ibss.ie_len = params->ie_len; | 913 | sdata->u.ibss.ie_len = params->ie_len; |
923 | } | 914 | } |
924 | 915 | ||
925 | skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom + | ||
926 | 36 /* bitrates */ + | ||
927 | 34 /* SSID */ + | ||
928 | 3 /* DS params */ + | ||
929 | 4 /* IBSS params */ + | ||
930 | params->ie_len); | ||
931 | if (!skb) | ||
932 | return -ENOMEM; | ||
933 | |||
934 | sdata->u.ibss.skb = skb; | 916 | sdata->u.ibss.skb = skb; |
935 | sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; | 917 | sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH; |
936 | sdata->u.ibss.ibss_join_req = jiffies; | 918 | sdata->u.ibss.ibss_join_req = jiffies; |
937 | 919 | ||
938 | memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); | 920 | memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN); |
939 | |||
940 | /* | ||
941 | * The ssid_len setting below is used to see whether | ||
942 | * we are active, and we need all other settings | ||
943 | * before that may get visible. | ||
944 | */ | ||
945 | mb(); | ||
946 | |||
947 | sdata->u.ibss.ssid_len = params->ssid_len; | 921 | sdata->u.ibss.ssid_len = params->ssid_len; |
948 | 922 | ||
949 | ieee80211_recalc_idle(sdata->local); | 923 | ieee80211_recalc_idle(sdata->local); |
950 | 924 | ||
951 | set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); | 925 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); |
952 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.ibss.work); | 926 | |
927 | mutex_unlock(&sdata->u.ibss.mtx); | ||
953 | 928 | ||
954 | return 0; | 929 | return 0; |
955 | } | 930 | } |
@@ -957,11 +932,33 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
957 | int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) | 932 | int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) |
958 | { | 933 | { |
959 | struct sk_buff *skb; | 934 | struct sk_buff *skb; |
935 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
936 | struct ieee80211_local *local = sdata->local; | ||
937 | struct cfg80211_bss *cbss; | ||
938 | u16 capability; | ||
939 | int active_ibss; | ||
960 | 940 | ||
961 | del_timer_sync(&sdata->u.ibss.timer); | 941 | mutex_lock(&sdata->u.ibss.mtx); |
962 | clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); | 942 | |
963 | cancel_work_sync(&sdata->u.ibss.work); | 943 | active_ibss = ieee80211_sta_active_ibss(sdata); |
964 | clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request); | 944 | |
945 | if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { | ||
946 | capability = WLAN_CAPABILITY_IBSS; | ||
947 | |||
948 | if (ifibss->privacy) | ||
949 | capability |= WLAN_CAPABILITY_PRIVACY; | ||
950 | |||
951 | cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->channel, | ||
952 | ifibss->bssid, ifibss->ssid, | ||
953 | ifibss->ssid_len, WLAN_CAPABILITY_IBSS | | ||
954 | WLAN_CAPABILITY_PRIVACY, | ||
955 | capability); | ||
956 | |||
957 | if (cbss) { | ||
958 | cfg80211_unlink_bss(local->hw.wiphy, cbss); | ||
959 | cfg80211_put_bss(cbss); | ||
960 | } | ||
961 | } | ||
965 | 962 | ||
966 | sta_info_flush(sdata->local, sdata); | 963 | sta_info_flush(sdata->local, sdata); |
967 | 964 | ||
@@ -975,10 +972,14 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) | |||
975 | synchronize_rcu(); | 972 | synchronize_rcu(); |
976 | kfree_skb(skb); | 973 | kfree_skb(skb); |
977 | 974 | ||
978 | skb_queue_purge(&sdata->u.ibss.skb_queue); | 975 | skb_queue_purge(&sdata->skb_queue); |
979 | memset(sdata->u.ibss.bssid, 0, ETH_ALEN); | 976 | memset(sdata->u.ibss.bssid, 0, ETH_ALEN); |
980 | sdata->u.ibss.ssid_len = 0; | 977 | sdata->u.ibss.ssid_len = 0; |
981 | 978 | ||
979 | del_timer_sync(&sdata->u.ibss.timer); | ||
980 | |||
981 | mutex_unlock(&sdata->u.ibss.mtx); | ||
982 | |||
982 | ieee80211_recalc_idle(sdata->local); | 983 | ieee80211_recalc_idle(sdata->local); |
983 | 984 | ||
984 | return 0; | 985 | return 0; |