xref: /openbmc/linux/net/mac80211/chan.c (revision df2634f43f5106947f3735a0b61a6527a4b278cd)
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 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
24 			continue;
25 
26 		if (sdata->vif.type == NL80211_IFTYPE_STATION &&
27 		    !sdata->u.mgd.associated)
28 			continue;
29 
30 		if (sdata->vif.type == 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 		}
36 
37 		if (sdata->vif.type == NL80211_IFTYPE_AP &&
38 		    !sdata->u.ap.beacon)
39 			continue;
40 
41 		return CHAN_MODE_FIXED;
42 	}
43 
44 	return CHAN_MODE_UNDEFINED;
45 }
46 
47 enum ieee80211_chan_mode
48 ieee80211_get_channel_mode(struct ieee80211_local *local,
49 			   struct ieee80211_sub_if_data *ignore)
50 {
51 	enum ieee80211_chan_mode mode;
52 
53 	mutex_lock(&local->iflist_mtx);
54 	mode = __ieee80211_get_channel_mode(local, ignore);
55 	mutex_unlock(&local->iflist_mtx);
56 
57 	return mode;
58 }
59 
60 bool ieee80211_set_channel_type(struct ieee80211_local *local,
61 				struct ieee80211_sub_if_data *sdata,
62 				enum nl80211_channel_type chantype)
63 {
64 	struct ieee80211_sub_if_data *tmp;
65 	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
66 	bool result;
67 
68 	mutex_lock(&local->iflist_mtx);
69 
70 	list_for_each_entry(tmp, &local->interfaces, list) {
71 		if (tmp == sdata)
72 			continue;
73 
74 		if (!ieee80211_sdata_running(tmp))
75 			continue;
76 
77 		switch (tmp->vif.bss_conf.channel_type) {
78 		case NL80211_CHAN_NO_HT:
79 		case NL80211_CHAN_HT20:
80 			superchan = tmp->vif.bss_conf.channel_type;
81 			break;
82 		case NL80211_CHAN_HT40PLUS:
83 			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
84 			superchan = NL80211_CHAN_HT40PLUS;
85 			break;
86 		case NL80211_CHAN_HT40MINUS:
87 			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
88 			superchan = NL80211_CHAN_HT40MINUS;
89 			break;
90 		}
91 	}
92 
93 	switch (superchan) {
94 	case NL80211_CHAN_NO_HT:
95 	case NL80211_CHAN_HT20:
96 		/*
97 		 * allow any change that doesn't go to no-HT
98 		 * (if it already is no-HT no change is needed)
99 		 */
100 		if (chantype == NL80211_CHAN_NO_HT)
101 			break;
102 		superchan = chantype;
103 		break;
104 	case NL80211_CHAN_HT40PLUS:
105 	case NL80211_CHAN_HT40MINUS:
106 		/* allow smaller bandwidth and same */
107 		if (chantype == NL80211_CHAN_NO_HT)
108 			break;
109 		if (chantype == NL80211_CHAN_HT20)
110 			break;
111 		if (superchan == chantype)
112 			break;
113 		result = false;
114 		goto out;
115 	}
116 
117 	local->_oper_channel_type = superchan;
118 
119 	if (sdata)
120 		sdata->vif.bss_conf.channel_type = chantype;
121 
122 	result = true;
123  out:
124 	mutex_unlock(&local->iflist_mtx);
125 
126 	return result;
127 }
128