xref: /linux/net/mac80211/chan.c (revision 800c5eb7b5eba6cb2a32738d763fd59f0fbcdde4)
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 }
137