xref: /openbmc/linux/net/mac80211/chan.c (revision 63dc02bd)
1 /*
2  * mac80211 - channel management
3  */
4 
5 #include <linux/nl80211.h>
6 #include <net/cfg80211.h>
7 #include "ieee80211_i.h"
8 
9 static enum ieee80211_chan_mode
10 __ieee80211_get_channel_mode(struct ieee80211_local *local,
11 			     struct ieee80211_sub_if_data *ignore)
12 {
13 	struct ieee80211_sub_if_data *sdata;
14 
15 	lockdep_assert_held(&local->iflist_mtx);
16 
17 	list_for_each_entry(sdata, &local->interfaces, list) {
18 		if (sdata == ignore)
19 			continue;
20 
21 		if (!ieee80211_sdata_running(sdata))
22 			continue;
23 
24 		switch (sdata->vif.type) {
25 		case NL80211_IFTYPE_MONITOR:
26 			continue;
27 		case NL80211_IFTYPE_STATION:
28 			if (!sdata->u.mgd.associated)
29 				continue;
30 			break;
31 		case NL80211_IFTYPE_ADHOC:
32 			if (!sdata->u.ibss.ssid_len)
33 				continue;
34 			if (!sdata->u.ibss.fixed_channel)
35 				return CHAN_MODE_HOPPING;
36 			break;
37 		case NL80211_IFTYPE_AP_VLAN:
38 			/* will also have _AP interface */
39 			continue;
40 		case NL80211_IFTYPE_AP:
41 			if (!sdata->u.ap.beacon)
42 				continue;
43 			break;
44 		default:
45 			break;
46 		}
47 
48 		return CHAN_MODE_FIXED;
49 	}
50 
51 	return CHAN_MODE_UNDEFINED;
52 }
53 
54 enum ieee80211_chan_mode
55 ieee80211_get_channel_mode(struct ieee80211_local *local,
56 			   struct ieee80211_sub_if_data *ignore)
57 {
58 	enum ieee80211_chan_mode mode;
59 
60 	mutex_lock(&local->iflist_mtx);
61 	mode = __ieee80211_get_channel_mode(local, ignore);
62 	mutex_unlock(&local->iflist_mtx);
63 
64 	return mode;
65 }
66 
67 bool ieee80211_set_channel_type(struct ieee80211_local *local,
68 				struct ieee80211_sub_if_data *sdata,
69 				enum nl80211_channel_type chantype)
70 {
71 	struct ieee80211_sub_if_data *tmp;
72 	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
73 	bool result;
74 
75 	mutex_lock(&local->iflist_mtx);
76 
77 	list_for_each_entry(tmp, &local->interfaces, list) {
78 		if (tmp == sdata)
79 			continue;
80 
81 		if (!ieee80211_sdata_running(tmp))
82 			continue;
83 
84 		switch (tmp->vif.bss_conf.channel_type) {
85 		case NL80211_CHAN_NO_HT:
86 		case NL80211_CHAN_HT20:
87 			if (superchan > tmp->vif.bss_conf.channel_type)
88 				break;
89 
90 			superchan = tmp->vif.bss_conf.channel_type;
91 			break;
92 		case NL80211_CHAN_HT40PLUS:
93 			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
94 			superchan = NL80211_CHAN_HT40PLUS;
95 			break;
96 		case NL80211_CHAN_HT40MINUS:
97 			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
98 			superchan = NL80211_CHAN_HT40MINUS;
99 			break;
100 		}
101 	}
102 
103 	switch (superchan) {
104 	case NL80211_CHAN_NO_HT:
105 	case NL80211_CHAN_HT20:
106 		/*
107 		 * allow any change that doesn't go to no-HT
108 		 * (if it already is no-HT no change is needed)
109 		 */
110 		if (chantype == NL80211_CHAN_NO_HT)
111 			break;
112 		superchan = chantype;
113 		break;
114 	case NL80211_CHAN_HT40PLUS:
115 	case NL80211_CHAN_HT40MINUS:
116 		/* allow smaller bandwidth and same */
117 		if (chantype == NL80211_CHAN_NO_HT)
118 			break;
119 		if (chantype == NL80211_CHAN_HT20)
120 			break;
121 		if (superchan == chantype)
122 			break;
123 		result = false;
124 		goto out;
125 	}
126 
127 	local->_oper_channel_type = superchan;
128 
129 	if (sdata)
130 		sdata->vif.bss_conf.channel_type = chantype;
131 
132 	result = true;
133  out:
134 	mutex_unlock(&local->iflist_mtx);
135 
136 	return result;
137 }
138 
139 /*
140  * ieee80211_get_tx_channel_type returns the channel type we should
141  * use for packet transmission, given the channel capability and
142  * whatever regulatory flags we have been given.
143  */
144 enum nl80211_channel_type ieee80211_get_tx_channel_type(
145 				struct ieee80211_local *local,
146 				enum nl80211_channel_type channel_type)
147 {
148 	switch (channel_type) {
149 	case NL80211_CHAN_HT40PLUS:
150 		if (local->hw.conf.channel->flags &
151 				IEEE80211_CHAN_NO_HT40PLUS)
152 			return NL80211_CHAN_HT20;
153 		break;
154 	case NL80211_CHAN_HT40MINUS:
155 		if (local->hw.conf.channel->flags &
156 				IEEE80211_CHAN_NO_HT40MINUS)
157 			return NL80211_CHAN_HT20;
158 		break;
159 	default:
160 		break;
161 	}
162 	return channel_type;
163 }
164