xref: /linux/drivers/net/wireless/marvell/mwifiex/11h.c (revision 170aafe35cb98e0f3fbacb446ea86389fbce22ea)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * NXP Wireless LAN device driver: 802.11h
4  *
5  * Copyright 2011-2020 NXP
6  */
7 
8 #include "main.h"
9 #include "fw.h"
10 
11 
12 void mwifiex_init_11h_params(struct mwifiex_private *priv)
13 {
14 	priv->state_11h.is_11h_enabled = true;
15 	priv->state_11h.is_11h_active = false;
16 }
17 
18 inline int mwifiex_is_11h_active(struct mwifiex_private *priv)
19 {
20 	return priv->state_11h.is_11h_active;
21 }
22 /* This function appends 11h info to a buffer while joining an
23  * infrastructure BSS
24  */
25 static void
26 mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer,
27 			       struct mwifiex_bssdescriptor *bss_desc)
28 {
29 	struct mwifiex_ie_types_header *ie_header;
30 	struct mwifiex_ie_types_pwr_capability *cap;
31 	struct mwifiex_ie_types_local_pwr_constraint *constraint;
32 	struct ieee80211_supported_band *sband;
33 	u8 radio_type;
34 	int i;
35 
36 	if (!buffer || !(*buffer))
37 		return;
38 
39 	radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
40 	sband = priv->wdev.wiphy->bands[radio_type];
41 
42 	cap = (struct mwifiex_ie_types_pwr_capability *)*buffer;
43 	cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
44 	cap->header.len = cpu_to_le16(2);
45 	cap->min_pwr = 0;
46 	cap->max_pwr = 0;
47 	*buffer += sizeof(*cap);
48 
49 	constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer;
50 	constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
51 	constraint->header.len = cpu_to_le16(2);
52 	constraint->chan = bss_desc->channel;
53 	constraint->constraint = bss_desc->local_constraint;
54 	*buffer += sizeof(*constraint);
55 
56 	ie_header = (struct mwifiex_ie_types_header *)*buffer;
57 	ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
58 	ie_header->len  = cpu_to_le16(2 * sband->n_channels + 2);
59 	*buffer += sizeof(*ie_header);
60 	*(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
61 	*(*buffer)++ = 2 * sband->n_channels;
62 	for (i = 0; i < sband->n_channels; i++) {
63 		*(*buffer)++ = ieee80211_frequency_to_channel(
64 					sband->channels[i].center_freq);
65 		*(*buffer)++ = 1; /* one channel in the subband */
66 	}
67 }
68 
69 /* Enable or disable the 11h extensions in the firmware */
70 int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
71 {
72 	u32 enable = flag;
73 
74 	/* enable master mode radar detection on AP interface */
75 	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable)
76 		enable |= MWIFIEX_MASTER_RADAR_DET_MASK;
77 
78 	return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
79 				HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true);
80 }
81 
82 /* This functions processes TLV buffer for a pending BSS Join command.
83  *
84  * Activate 11h functionality in the firmware if the spectrum management
85  * capability bit is found in the network we are joining. Also, necessary
86  * TLVs are set based on requested network's 11h capability.
87  */
88 void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
89 			      struct mwifiex_bssdescriptor *bss_desc)
90 {
91 	if (bss_desc->sensed_11h) {
92 		/* Activate 11h functions in firmware, turns on capability
93 		 * bit
94 		 */
95 		mwifiex_11h_activate(priv, true);
96 		priv->state_11h.is_11h_active = true;
97 		bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
98 		mwifiex_11h_process_infra_join(priv, buffer, bss_desc);
99 	} else {
100 		/* Deactivate 11h functions in the firmware */
101 		mwifiex_11h_activate(priv, false);
102 		priv->state_11h.is_11h_active = false;
103 		bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
104 	}
105 }
106 
107 /* This is DFS CAC work queue function.
108  * This delayed work emits CAC finished event for cfg80211 if
109  * CAC was started earlier.
110  */
111 void mwifiex_dfs_cac_work_queue(struct work_struct *work)
112 {
113 	struct cfg80211_chan_def chandef;
114 	struct delayed_work *delayed_work = to_delayed_work(work);
115 	struct mwifiex_private *priv =
116 			container_of(delayed_work, struct mwifiex_private,
117 				     dfs_cac_work);
118 
119 	chandef = priv->dfs_chandef;
120 	if (priv->wdev.links[0].cac_started) {
121 		mwifiex_dbg(priv->adapter, MSG,
122 			    "CAC timer finished; No radar detected\n");
123 		cfg80211_cac_event(priv->netdev, &chandef,
124 				   NL80211_RADAR_CAC_FINISHED,
125 				   GFP_KERNEL, 0);
126 	}
127 }
128 
129 /* This function prepares channel report request command to FW for
130  * starting radar detection.
131  */
132 int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
133 					  struct host_cmd_ds_command *cmd,
134 					  void *data_buf)
135 {
136 	struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req;
137 	struct mwifiex_radar_params *radar_params = (void *)data_buf;
138 
139 	cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST);
140 	cmd->size = cpu_to_le16(S_DS_GEN);
141 	le16_unaligned_add_cpu(&cmd->size,
142 			       sizeof(struct host_cmd_ds_chan_rpt_req));
143 
144 	cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ);
145 	cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value;
146 	cr_req->chan_desc.chan_width = radar_params->chandef->width;
147 	cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms);
148 
149 	if (radar_params->cac_time_ms)
150 		mwifiex_dbg(priv->adapter, MSG,
151 			    "11h: issuing DFS Radar check for channel=%d\n",
152 			    radar_params->chandef->chan->hw_value);
153 	else
154 		mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n");
155 
156 	return 0;
157 }
158 
159 int mwifiex_stop_radar_detection(struct mwifiex_private *priv,
160 				 struct cfg80211_chan_def *chandef)
161 {
162 	struct mwifiex_radar_params radar_params;
163 
164 	memset(&radar_params, 0, sizeof(struct mwifiex_radar_params));
165 	radar_params.chandef = chandef;
166 	radar_params.cac_time_ms = 0;
167 
168 	return mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
169 				HostCmd_ACT_GEN_SET, 0, &radar_params, true);
170 }
171 
172 /* This function is to abort ongoing CAC upon stopping AP operations
173  * or during unload.
174  */
175 void mwifiex_abort_cac(struct mwifiex_private *priv)
176 {
177 	if (priv->wdev.links[0].cac_started) {
178 		if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef))
179 			mwifiex_dbg(priv->adapter, ERROR,
180 				    "failed to stop CAC in FW\n");
181 		mwifiex_dbg(priv->adapter, MSG,
182 			    "Aborting delayed work for CAC.\n");
183 		cancel_delayed_work_sync(&priv->dfs_cac_work);
184 		cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
185 				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL,
186 				   0);
187 	}
188 }
189 
190 /* This function handles channel report event from FW during CAC period.
191  * If radar is detected during CAC, driver indicates the same to cfg80211
192  * and also cancels ongoing delayed work.
193  */
194 int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv,
195 				     struct sk_buff *skb)
196 {
197 	struct host_cmd_ds_chan_rpt_event *rpt_event;
198 	struct mwifiex_ie_types_chan_rpt_data *rpt;
199 	u16 event_len, tlv_len;
200 
201 	rpt_event = (void *)(skb->data + sizeof(u32));
202 	event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+
203 				sizeof(u32));
204 
205 	if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) {
206 		mwifiex_dbg(priv->adapter, ERROR,
207 			    "Error in channel report event\n");
208 		return -1;
209 	}
210 
211 	while (event_len >= sizeof(struct mwifiex_ie_types_header)) {
212 		rpt = (void *)&rpt_event->tlvbuf;
213 		tlv_len = le16_to_cpu(rpt->header.len);
214 
215 		switch (le16_to_cpu(rpt->header.type)) {
216 		case TLV_TYPE_CHANRPT_11H_BASIC:
217 			if (rpt->map.radar) {
218 				mwifiex_dbg(priv->adapter, MSG,
219 					    "RADAR Detected on channel %d!\n",
220 					    priv->dfs_chandef.chan->hw_value);
221 				cancel_delayed_work_sync(&priv->dfs_cac_work);
222 				cfg80211_cac_event(priv->netdev,
223 						   &priv->dfs_chandef,
224 						   NL80211_RADAR_DETECTED,
225 						   GFP_KERNEL, 0);
226 			}
227 			break;
228 		default:
229 			break;
230 		}
231 
232 		event_len -= (tlv_len + sizeof(rpt->header));
233 	}
234 
235 	return 0;
236 }
237 
238 /* Handler for radar detected event from FW.*/
239 int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv,
240 				      struct sk_buff *skb)
241 {
242 	struct mwifiex_radar_det_event *rdr_event;
243 
244 	rdr_event = (void *)(skb->data + sizeof(u32));
245 
246 	mwifiex_dbg(priv->adapter, MSG,
247 		    "radar detected; indicating kernel\n");
248 	if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef))
249 		mwifiex_dbg(priv->adapter, ERROR,
250 			    "Failed to stop CAC in FW\n");
251 	cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef,
252 			     GFP_KERNEL);
253 	mwifiex_dbg(priv->adapter, MSG, "regdomain: %d\n",
254 		    rdr_event->reg_domain);
255 	mwifiex_dbg(priv->adapter, MSG, "radar detection type: %d\n",
256 		    rdr_event->det_type);
257 
258 	return 0;
259 }
260 
261 /* This is work queue function for channel switch handling.
262  * This function takes care of updating new channel definitin to
263  * bss config structure, restart AP and indicate channel switch success
264  * to cfg80211.
265  */
266 void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work)
267 {
268 	struct mwifiex_uap_bss_param *bss_cfg;
269 	struct delayed_work *delayed_work = to_delayed_work(work);
270 	struct mwifiex_private *priv =
271 			container_of(delayed_work, struct mwifiex_private,
272 				     dfs_chan_sw_work);
273 
274 	bss_cfg = &priv->bss_cfg;
275 	if (!bss_cfg->beacon_period) {
276 		mwifiex_dbg(priv->adapter, ERROR,
277 			    "channel switch: AP already stopped\n");
278 		return;
279 	}
280 
281 	mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef);
282 
283 	if (mwifiex_config_start_uap(priv, bss_cfg)) {
284 		mwifiex_dbg(priv->adapter, ERROR,
285 			    "Failed to start AP after channel switch\n");
286 		return;
287 	}
288 
289 	mwifiex_dbg(priv->adapter, MSG,
290 		    "indicating channel switch completion to kernel\n");
291 	wiphy_lock(priv->wdev.wiphy);
292 	cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0);
293 	wiphy_unlock(priv->wdev.wiphy);
294 }
295