blob: d1f7abddb182e1fe1f780af0d19e1948ca3c70e6 [file] [log] [blame]
Johannes Bergf444de02010-05-05 15:25:02 +02001/*
2 * mac80211 - channel management
3 */
4
Johannes Berg0aaffa92010-05-05 15:28:27 +02005#include <linux/nl80211.h>
Johannes Bergf444de02010-05-05 15:25:02 +02006#include "ieee80211_i.h"
7
Johannes Berg368a07d2010-05-28 14:26:23 +02008static enum ieee80211_chan_mode
Johannes Bergf444de02010-05-05 15:25:02 +02009__ieee80211_get_channel_mode(struct ieee80211_local *local,
10 struct ieee80211_sub_if_data *ignore)
11{
12 struct ieee80211_sub_if_data *sdata;
13
Johannes Berg46a5eba2010-09-15 13:28:15 +020014 lockdep_assert_held(&local->iflist_mtx);
Johannes Bergf444de02010-05-05 15:25:02 +020015
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
Johannes Berge9980e62012-01-09 13:57:36 +010023 switch (sdata->vif.type) {
24 case NL80211_IFTYPE_MONITOR:
Johannes Bergf444de02010-05-05 15:25:02 +020025 continue;
Johannes Berge9980e62012-01-09 13:57:36 +010026 case NL80211_IFTYPE_STATION:
27 if (!sdata->u.mgd.associated)
28 continue;
29 break;
30 case NL80211_IFTYPE_ADHOC:
Johannes Bergf444de02010-05-05 15:25:02 +020031 if (!sdata->u.ibss.ssid_len)
32 continue;
33 if (!sdata->u.ibss.fixed_channel)
34 return CHAN_MODE_HOPPING;
Johannes Berge9980e62012-01-09 13:57:36 +010035 break;
36 case NL80211_IFTYPE_AP_VLAN:
37 /* will also have _AP interface */
Johannes Bergf444de02010-05-05 15:25:02 +020038 continue;
Johannes Berge9980e62012-01-09 13:57:36 +010039 case NL80211_IFTYPE_AP:
40 if (!sdata->u.ap.beacon)
41 continue;
42 break;
43 default:
44 break;
45 }
Johannes Bergf444de02010-05-05 15:25:02 +020046
47 return CHAN_MODE_FIXED;
48 }
49
50 return CHAN_MODE_UNDEFINED;
51}
52
53enum ieee80211_chan_mode
54ieee80211_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}
Johannes Berg0aaffa92010-05-05 15:28:27 +020065
66bool 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:
Felix Fietkau9db372f2011-03-11 21:45:51 +010086 if (superchan > tmp->vif.bss_conf.channel_type)
87 break;
88
Johannes Berg0aaffa92010-05-05 15:28:27 +020089 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}