Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[linux-2.6.git] / net / mac80211 / chan.c
1 /*
2  * mac80211 - channel management
3  */
4
5 #include <linux/nl80211.h>
6 #include "ieee80211_i.h"
7
8 static enum ieee80211_chan_mode
9 __ieee80211_get_channel_mode(struct ieee80211_local *local,
10                              struct ieee80211_sub_if_data *ignore)
11 {
12         struct ieee80211_sub_if_data *sdata;
13
14         lockdep_assert_held(&local->iflist_mtx);
15
16         list_for_each_entry(sdata, &local->interfaces, list) {
17                 if (sdata == ignore)
18                         continue;
19
20                 if (!ieee80211_sdata_running(sdata))
21                         continue;
22
23                 switch (sdata->vif.type) {
24                 case NL80211_IFTYPE_MONITOR:
25                         continue;
26                 case NL80211_IFTYPE_STATION:
27                         if (!sdata->u.mgd.associated)
28                                 continue;
29                         break;
30                 case NL80211_IFTYPE_ADHOC:
31                         if (!sdata->u.ibss.ssid_len)
32                                 continue;
33                         if (!sdata->u.ibss.fixed_channel)
34                                 return CHAN_MODE_HOPPING;
35                         break;
36                 case NL80211_IFTYPE_AP_VLAN:
37                         /* will also have _AP interface */
38                         continue;
39                 case NL80211_IFTYPE_AP:
40                         if (!sdata->u.ap.beacon)
41                                 continue;
42                         break;
43                 default:
44                         break;
45                 }
46
47                 return CHAN_MODE_FIXED;
48         }
49
50         return CHAN_MODE_UNDEFINED;
51 }
52
53 enum ieee80211_chan_mode
54 ieee80211_get_channel_mode(struct ieee80211_local *local,
55                            struct ieee80211_sub_if_data *ignore)
56 {
57         enum ieee80211_chan_mode mode;
58
59         mutex_lock(&local->iflist_mtx);
60         mode = __ieee80211_get_channel_mode(local, ignore);
61         mutex_unlock(&local->iflist_mtx);
62
63         return mode;
64 }
65
66 bool ieee80211_set_channel_type(struct ieee80211_local *local,
67                                 struct ieee80211_sub_if_data *sdata,
68                                 enum nl80211_channel_type chantype)
69 {
70         struct ieee80211_sub_if_data *tmp;
71         enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
72         bool result;
73
74         mutex_lock(&local->iflist_mtx);
75
76         list_for_each_entry(tmp, &local->interfaces, list) {
77                 if (tmp == sdata)
78                         continue;
79
80                 if (!ieee80211_sdata_running(tmp))
81                         continue;
82
83                 switch (tmp->vif.bss_conf.channel_type) {
84                 case NL80211_CHAN_NO_HT:
85                 case NL80211_CHAN_HT20:
86                         if (superchan > tmp->vif.bss_conf.channel_type)
87                                 break;
88
89                         superchan = tmp->vif.bss_conf.channel_type;
90                         break;
91                 case NL80211_CHAN_HT40PLUS:
92                         WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
93                         superchan = NL80211_CHAN_HT40PLUS;
94                         break;
95                 case NL80211_CHAN_HT40MINUS:
96                         WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
97                         superchan = NL80211_CHAN_HT40MINUS;
98                         break;
99                 }
100         }
101
102         switch (superchan) {
103         case NL80211_CHAN_NO_HT:
104         case NL80211_CHAN_HT20:
105                 /*
106                  * allow any change that doesn't go to no-HT
107                  * (if it already is no-HT no change is needed)
108                  */
109                 if (chantype == NL80211_CHAN_NO_HT)
110                         break;
111                 superchan = chantype;
112                 break;
113         case NL80211_CHAN_HT40PLUS:
114         case NL80211_CHAN_HT40MINUS:
115                 /* allow smaller bandwidth and same */
116                 if (chantype == NL80211_CHAN_NO_HT)
117                         break;
118                 if (chantype == NL80211_CHAN_HT20)
119                         break;
120                 if (superchan == chantype)
121                         break;
122                 result = false;
123                 goto out;
124         }
125
126         local->_oper_channel_type = superchan;
127
128         if (sdata)
129                 sdata->vif.bss_conf.channel_type = chantype;
130
131         result = true;
132  out:
133         mutex_unlock(&local->iflist_mtx);
134
135         return result;
136 }