chan.c 3.7 KB
Newer Older
1 2 3 4
/*
 * mac80211 - channel management
 */

5
#include <linux/nl80211.h>
6
#include <net/cfg80211.h>
7 8
#include "ieee80211_i.h"

J
Johannes Berg 已提交
9
static enum ieee80211_chan_mode
10 11 12 13 14
__ieee80211_get_channel_mode(struct ieee80211_local *local,
			     struct ieee80211_sub_if_data *ignore)
{
	struct ieee80211_sub_if_data *sdata;

15
	lockdep_assert_held(&local->iflist_mtx);
16 17 18 19 20 21 22 23

	list_for_each_entry(sdata, &local->interfaces, list) {
		if (sdata == ignore)
			continue;

		if (!ieee80211_sdata_running(sdata))
			continue;

24 25
		switch (sdata->vif.type) {
		case NL80211_IFTYPE_MONITOR:
26
			continue;
27 28 29 30 31
		case NL80211_IFTYPE_STATION:
			if (!sdata->u.mgd.associated)
				continue;
			break;
		case NL80211_IFTYPE_ADHOC:
32 33 34 35
			if (!sdata->u.ibss.ssid_len)
				continue;
			if (!sdata->u.ibss.fixed_channel)
				return CHAN_MODE_HOPPING;
36 37 38
			break;
		case NL80211_IFTYPE_AP_VLAN:
			/* will also have _AP interface */
39
			continue;
40 41 42 43
		case NL80211_IFTYPE_AP:
			if (!sdata->u.ap.beacon)
				continue;
			break;
44 45 46 47
		case NL80211_IFTYPE_MESH_POINT:
			if (!sdata->wdev.mesh_id_len)
				continue;
			break;
48 49 50
		default:
			break;
		}
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

		return CHAN_MODE_FIXED;
	}

	return CHAN_MODE_UNDEFINED;
}

enum ieee80211_chan_mode
ieee80211_get_channel_mode(struct ieee80211_local *local,
			   struct ieee80211_sub_if_data *ignore)
{
	enum ieee80211_chan_mode mode;

	mutex_lock(&local->iflist_mtx);
	mode = __ieee80211_get_channel_mode(local, ignore);
	mutex_unlock(&local->iflist_mtx);

	return mode;
}
70

71 72 73
static enum nl80211_channel_type
ieee80211_get_superchan(struct ieee80211_local *local,
			struct ieee80211_sub_if_data *sdata)
74 75
{
	enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
76
	struct ieee80211_sub_if_data *tmp;
77 78 79 80 81 82 83 84 85 86 87 88

	mutex_lock(&local->iflist_mtx);
	list_for_each_entry(tmp, &local->interfaces, list) {
		if (tmp == sdata)
			continue;

		if (!ieee80211_sdata_running(tmp))
			continue;

		switch (tmp->vif.bss_conf.channel_type) {
		case NL80211_CHAN_NO_HT:
		case NL80211_CHAN_HT20:
89 90 91
			if (superchan > tmp->vif.bss_conf.channel_type)
				break;

92 93 94 95 96 97 98 99 100 101 102 103
			superchan = tmp->vif.bss_conf.channel_type;
			break;
		case NL80211_CHAN_HT40PLUS:
			WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
			superchan = NL80211_CHAN_HT40PLUS;
			break;
		case NL80211_CHAN_HT40MINUS:
			WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
			superchan = NL80211_CHAN_HT40MINUS;
			break;
		}
	}
104
	mutex_unlock(&local->iflist_mtx);
105

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	return superchan;
}

static bool
ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
				       enum nl80211_channel_type chantype2,
				       enum nl80211_channel_type *compat)
{
	/*
	 * start out with chantype1 being the result,
	 * overwriting later if needed
	 */
	if (compat)
		*compat = chantype1;

	switch (chantype1) {
122
	case NL80211_CHAN_NO_HT:
123 124 125
		if (compat)
			*compat = chantype2;
		break;
126 127 128 129 130
	case NL80211_CHAN_HT20:
		/*
		 * allow any change that doesn't go to no-HT
		 * (if it already is no-HT no change is needed)
		 */
131
		if (chantype2 == NL80211_CHAN_NO_HT)
132
			break;
133 134
		if (compat)
			*compat = chantype2;
135 136 137 138
		break;
	case NL80211_CHAN_HT40PLUS:
	case NL80211_CHAN_HT40MINUS:
		/* allow smaller bandwidth and same */
139
		if (chantype2 == NL80211_CHAN_NO_HT)
140
			break;
141
		if (chantype2 == NL80211_CHAN_HT20)
142
			break;
143
		if (chantype2 == chantype1)
144
			break;
145
		return false;
146 147
	}

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	return true;
}

bool ieee80211_set_channel_type(struct ieee80211_local *local,
				struct ieee80211_sub_if_data *sdata,
				enum nl80211_channel_type chantype)
{
	enum nl80211_channel_type superchan;
	enum nl80211_channel_type compatchan;

	superchan = ieee80211_get_superchan(local, sdata);
	if (!ieee80211_channel_types_are_compatible(superchan, chantype,
						    &compatchan))
		return false;

	local->_oper_channel_type = compatchan;
164 165 166 167

	if (sdata)
		sdata->vif.bss_conf.channel_type = chantype;

168
	return true;
169 170

}