xref: /openbmc/linux/net/mac80211/chan.c (revision 23a85b45)
1f444de05SJohannes Berg /*
2f444de05SJohannes Berg  * mac80211 - channel management
3f444de05SJohannes Berg  */
4f444de05SJohannes Berg 
50aaffa9bSJohannes Berg #include <linux/nl80211.h>
63117bbdbSPaul Stewart #include <net/cfg80211.h>
7f444de05SJohannes Berg #include "ieee80211_i.h"
8f444de05SJohannes Berg 
9368a07d2SJohannes Berg static enum ieee80211_chan_mode
10f444de05SJohannes Berg __ieee80211_get_channel_mode(struct ieee80211_local *local,
11f444de05SJohannes Berg 			     struct ieee80211_sub_if_data *ignore)
12f444de05SJohannes Berg {
13f444de05SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
14f444de05SJohannes Berg 
1546a5ebafSJohannes Berg 	lockdep_assert_held(&local->iflist_mtx);
16f444de05SJohannes Berg 
17f444de05SJohannes Berg 	list_for_each_entry(sdata, &local->interfaces, list) {
18f444de05SJohannes Berg 		if (sdata == ignore)
19f444de05SJohannes Berg 			continue;
20f444de05SJohannes Berg 
21f444de05SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
22f444de05SJohannes Berg 			continue;
23f444de05SJohannes Berg 
24e9980e6dSJohannes Berg 		switch (sdata->vif.type) {
25e9980e6dSJohannes Berg 		case NL80211_IFTYPE_MONITOR:
26f444de05SJohannes Berg 			continue;
27e9980e6dSJohannes Berg 		case NL80211_IFTYPE_STATION:
28e9980e6dSJohannes Berg 			if (!sdata->u.mgd.associated)
29f444de05SJohannes Berg 				continue;
30e9980e6dSJohannes Berg 			break;
31e9980e6dSJohannes Berg 		case NL80211_IFTYPE_ADHOC:
32f444de05SJohannes Berg 			if (!sdata->u.ibss.ssid_len)
33f444de05SJohannes Berg 				continue;
34f444de05SJohannes Berg 			if (!sdata->u.ibss.fixed_channel)
35f444de05SJohannes Berg 				return CHAN_MODE_HOPPING;
36e9980e6dSJohannes Berg 			break;
37e9980e6dSJohannes Berg 		case NL80211_IFTYPE_AP_VLAN:
38e9980e6dSJohannes Berg 			/* will also have _AP interface */
39f444de05SJohannes Berg 			continue;
40e9980e6dSJohannes Berg 		case NL80211_IFTYPE_AP:
41e9980e6dSJohannes Berg 			if (!sdata->u.ap.beacon)
42e9980e6dSJohannes Berg 				continue;
43e9980e6dSJohannes Berg 			break;
44be0f4237SThomas Pedersen 		case NL80211_IFTYPE_MESH_POINT:
45be0f4237SThomas Pedersen 			if (!sdata->wdev.mesh_id_len)
46be0f4237SThomas Pedersen 				continue;
47be0f4237SThomas Pedersen 			break;
48e9980e6dSJohannes Berg 		default:
49e9980e6dSJohannes Berg 			break;
50e9980e6dSJohannes Berg 		}
51f444de05SJohannes Berg 
52f444de05SJohannes Berg 		return CHAN_MODE_FIXED;
53f444de05SJohannes Berg 	}
54f444de05SJohannes Berg 
55f444de05SJohannes Berg 	return CHAN_MODE_UNDEFINED;
56f444de05SJohannes Berg }
57f444de05SJohannes Berg 
58f444de05SJohannes Berg enum ieee80211_chan_mode
59f444de05SJohannes Berg ieee80211_get_channel_mode(struct ieee80211_local *local,
60f444de05SJohannes Berg 			   struct ieee80211_sub_if_data *ignore)
61f444de05SJohannes Berg {
62f444de05SJohannes Berg 	enum ieee80211_chan_mode mode;
63f444de05SJohannes Berg 
64f444de05SJohannes Berg 	mutex_lock(&local->iflist_mtx);
65f444de05SJohannes Berg 	mode = __ieee80211_get_channel_mode(local, ignore);
66f444de05SJohannes Berg 	mutex_unlock(&local->iflist_mtx);
67f444de05SJohannes Berg 
68f444de05SJohannes Berg 	return mode;
69f444de05SJohannes Berg }
700aaffa9bSJohannes Berg 
7123a85b45SMichal Kazior static enum nl80211_channel_type
7223a85b45SMichal Kazior ieee80211_get_superchan(struct ieee80211_local *local,
7323a85b45SMichal Kazior 			struct ieee80211_sub_if_data *sdata)
740aaffa9bSJohannes Berg {
750aaffa9bSJohannes Berg 	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
7623a85b45SMichal Kazior 	struct ieee80211_sub_if_data *tmp;
770aaffa9bSJohannes Berg 
780aaffa9bSJohannes Berg 	mutex_lock(&local->iflist_mtx);
790aaffa9bSJohannes Berg 	list_for_each_entry(tmp, &local->interfaces, list) {
800aaffa9bSJohannes Berg 		if (tmp == sdata)
810aaffa9bSJohannes Berg 			continue;
820aaffa9bSJohannes Berg 
830aaffa9bSJohannes Berg 		if (!ieee80211_sdata_running(tmp))
840aaffa9bSJohannes Berg 			continue;
850aaffa9bSJohannes Berg 
860aaffa9bSJohannes Berg 		switch (tmp->vif.bss_conf.channel_type) {
870aaffa9bSJohannes Berg 		case NL80211_CHAN_NO_HT:
880aaffa9bSJohannes Berg 		case NL80211_CHAN_HT20:
899db372fdSFelix Fietkau 			if (superchan > tmp->vif.bss_conf.channel_type)
909db372fdSFelix Fietkau 				break;
919db372fdSFelix Fietkau 
920aaffa9bSJohannes Berg 			superchan = tmp->vif.bss_conf.channel_type;
930aaffa9bSJohannes Berg 			break;
940aaffa9bSJohannes Berg 		case NL80211_CHAN_HT40PLUS:
950aaffa9bSJohannes Berg 			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
960aaffa9bSJohannes Berg 			superchan = NL80211_CHAN_HT40PLUS;
970aaffa9bSJohannes Berg 			break;
980aaffa9bSJohannes Berg 		case NL80211_CHAN_HT40MINUS:
990aaffa9bSJohannes Berg 			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
1000aaffa9bSJohannes Berg 			superchan = NL80211_CHAN_HT40MINUS;
1010aaffa9bSJohannes Berg 			break;
1020aaffa9bSJohannes Berg 		}
1030aaffa9bSJohannes Berg 	}
10423a85b45SMichal Kazior 	mutex_unlock(&local->iflist_mtx);
1050aaffa9bSJohannes Berg 
10623a85b45SMichal Kazior 	return superchan;
10723a85b45SMichal Kazior }
10823a85b45SMichal Kazior 
10923a85b45SMichal Kazior static bool
11023a85b45SMichal Kazior ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
11123a85b45SMichal Kazior 				       enum nl80211_channel_type chantype2,
11223a85b45SMichal Kazior 				       enum nl80211_channel_type *compat)
11323a85b45SMichal Kazior {
11423a85b45SMichal Kazior 	/*
11523a85b45SMichal Kazior 	 * start out with chantype1 being the result,
11623a85b45SMichal Kazior 	 * overwriting later if needed
11723a85b45SMichal Kazior 	 */
11823a85b45SMichal Kazior 	if (compat)
11923a85b45SMichal Kazior 		*compat = chantype1;
12023a85b45SMichal Kazior 
12123a85b45SMichal Kazior 	switch (chantype1) {
1220aaffa9bSJohannes Berg 	case NL80211_CHAN_NO_HT:
12323a85b45SMichal Kazior 		if (compat)
12423a85b45SMichal Kazior 			*compat = chantype2;
12523a85b45SMichal Kazior 		break;
1260aaffa9bSJohannes Berg 	case NL80211_CHAN_HT20:
1270aaffa9bSJohannes Berg 		/*
1280aaffa9bSJohannes Berg 		 * allow any change that doesn't go to no-HT
1290aaffa9bSJohannes Berg 		 * (if it already is no-HT no change is needed)
1300aaffa9bSJohannes Berg 		 */
13123a85b45SMichal Kazior 		if (chantype2 == NL80211_CHAN_NO_HT)
1320aaffa9bSJohannes Berg 			break;
13323a85b45SMichal Kazior 		if (compat)
13423a85b45SMichal Kazior 			*compat = chantype2;
1350aaffa9bSJohannes Berg 		break;
1360aaffa9bSJohannes Berg 	case NL80211_CHAN_HT40PLUS:
1370aaffa9bSJohannes Berg 	case NL80211_CHAN_HT40MINUS:
1380aaffa9bSJohannes Berg 		/* allow smaller bandwidth and same */
13923a85b45SMichal Kazior 		if (chantype2 == NL80211_CHAN_NO_HT)
1400aaffa9bSJohannes Berg 			break;
14123a85b45SMichal Kazior 		if (chantype2 == NL80211_CHAN_HT20)
1420aaffa9bSJohannes Berg 			break;
14323a85b45SMichal Kazior 		if (chantype2 == chantype1)
1440aaffa9bSJohannes Berg 			break;
14523a85b45SMichal Kazior 		return false;
1460aaffa9bSJohannes Berg 	}
1470aaffa9bSJohannes Berg 
14823a85b45SMichal Kazior 	return true;
14923a85b45SMichal Kazior }
15023a85b45SMichal Kazior 
15123a85b45SMichal Kazior bool ieee80211_set_channel_type(struct ieee80211_local *local,
15223a85b45SMichal Kazior 				struct ieee80211_sub_if_data *sdata,
15323a85b45SMichal Kazior 				enum nl80211_channel_type chantype)
15423a85b45SMichal Kazior {
15523a85b45SMichal Kazior 	enum nl80211_channel_type superchan;
15623a85b45SMichal Kazior 	enum nl80211_channel_type compatchan;
15723a85b45SMichal Kazior 
15823a85b45SMichal Kazior 	superchan = ieee80211_get_superchan(local, sdata);
15923a85b45SMichal Kazior 	if (!ieee80211_channel_types_are_compatible(superchan, chantype,
16023a85b45SMichal Kazior 						    &compatchan))
16123a85b45SMichal Kazior 		return false;
16223a85b45SMichal Kazior 
16323a85b45SMichal Kazior 	local->_oper_channel_type = compatchan;
1640aaffa9bSJohannes Berg 
1650aaffa9bSJohannes Berg 	if (sdata)
1660aaffa9bSJohannes Berg 		sdata->vif.bss_conf.channel_type = chantype;
1670aaffa9bSJohannes Berg 
16823a85b45SMichal Kazior 	return true;
1690aaffa9bSJohannes Berg 
1700aaffa9bSJohannes Berg }
171