xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/nxp/mlan/mlan_meas.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /**
2  * @file mlan_meas.c
3  *
4  *  @brief Implementation of measurement interface code with the app/firmware
5  *
6  *  Driver implementation for sending and retrieving measurement requests
7  *    and responses.
8  *
9  *  Current use is limited to 802.11h.
10  *
11  *  Requires use of the following preprocessor define:
12  *    - ENABLE_MEAS
13  *
14  *
15  *  Copyright 2008-2021 NXP
16  *
17  *  This software file (the File) is distributed by NXP
18  *  under the terms of the GNU General Public License Version 2, June 1991
19  *  (the License).  You may use, redistribute and/or modify the File in
20  *  accordance with the terms and conditions of the License, a copy of which
21  *  is available by writing to the Free Software Foundation, Inc.,
22  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
23  *  worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
24  *
25  *  THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
26  *  IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
27  *  ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
28  *  this warranty disclaimer.
29  *
30  */
31 
32 /*************************************************************
33 Change Log:
34     03/24/2009: initial version
35 ************************************************************/
36 
37 #include "mlan.h"
38 #include "mlan_join.h"
39 #include "mlan_util.h"
40 #include "mlan_fw.h"
41 #include "mlan_main.h"
42 #include "mlan_ioctl.h"
43 #include "mlan_meas.h"
44 
45 #ifdef DEBUG_LEVEL2
46 /** String descriptions of the different measurement enums.  Debug display */
47 static const char *meas_type_str[WLAN_MEAS_NUM_TYPES] = {
48 	"basic",
49 };
50 
51 /********************************************************
52 			Local Functions
53 ********************************************************/
54 
55 /**
56  *  @brief Retrieve the measurement string representation of a meas_type enum
57  *  Used for debug display only
58  *
59  *  @param meas_type Measurement type enumeration input for string lookup
60  *
61  *  @return         Constant string representing measurement type
62  */
wlan_meas_get_meas_type_str(MeasType_t meas_type)63 static const char *wlan_meas_get_meas_type_str(MeasType_t meas_type)
64 {
65 	if (meas_type <= WLAN_MEAS_11H_MAX_TYPE)
66 		return meas_type_str[meas_type];
67 
68 	return "Invld";
69 }
70 #endif
71 
72 /**
73  *  @brief Debug print display of the input measurement request
74  *
75  *  @param pmeas_req  Pointer to the measurement request to display
76  *
77  *  @return          N/A
78  */
79 static void
wlan_meas_dump_meas_req(const HostCmd_DS_MEASUREMENT_REQUEST * pmeas_req)80 wlan_meas_dump_meas_req(const HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req)
81 {
82 	ENTER();
83 
84 	PRINTM(MINFO, "Meas: Req: ------------------------------\n");
85 
86 	PRINTM(MINFO, "Meas: Req: mac_addr: " MACSTR "\n",
87 	       MAC2STR(pmeas_req->mac_addr));
88 
89 	PRINTM(MINFO, "Meas: Req:  dlgTkn: %d\n", pmeas_req->dialog_token);
90 	PRINTM(MINFO, "Meas: Req:    mode: dm[%c] rpt[%c] req[%c]\n",
91 	       pmeas_req->req_mode.duration_mandatory ? 'X' : ' ',
92 	       pmeas_req->req_mode.report ? 'X' : ' ',
93 	       pmeas_req->req_mode.request ? 'X' : ' ');
94 	PRINTM(MINFO, "Meas: Req:        : en[%c] par[%c]\n",
95 	       pmeas_req->req_mode.enable ? 'X' : ' ',
96 	       pmeas_req->req_mode.parallel ? 'X' : ' ');
97 #ifdef DEBUG_LEVEL2
98 	PRINTM(MINFO, "Meas: Req: measTyp: %s\n",
99 	       wlan_meas_get_meas_type_str(pmeas_req->meas_type));
100 #endif
101 
102 	switch (pmeas_req->meas_type) {
103 	case WLAN_MEAS_BASIC:
104 		/* Lazy cheat, fields of bas, cca, rpi union match on the
105 		 * request */
106 		PRINTM(MINFO, "Meas: Req: chan: %u\n",
107 		       pmeas_req->req.basic.channel);
108 		PRINTM(MINFO, "Meas: Req: strt: %llu\n",
109 		       wlan_le64_to_cpu(pmeas_req->req.basic.start_time));
110 		PRINTM(MINFO, "Meas: Req:  dur: %u\n",
111 		       wlan_le16_to_cpu(pmeas_req->req.basic.duration));
112 		break;
113 	default:
114 		PRINTM(MINFO, "Meas: Req: <unhandled>\n");
115 		break;
116 	}
117 
118 	PRINTM(MINFO, "Meas: Req: ------------------------------\n");
119 	LEAVE();
120 }
121 
122 /**
123  *  @brief Debug print display of the input measurement report
124  *
125  *  @param pmeas_rpt  Pointer to measurement report to display
126  *
127  *  @return          N/A
128  */
129 static void
wlan_meas_dump_meas_rpt(const HostCmd_DS_MEASUREMENT_REPORT * pmeas_rpt)130 wlan_meas_dump_meas_rpt(const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt)
131 {
132 	MeasType_t type;
133 	ENTER();
134 
135 	PRINTM(MINFO, "Meas: Rpt: ------------------------------\n");
136 	PRINTM(MINFO, "Meas: Rpt: mac_addr: " MACSTR "\n",
137 	       MAC2STR(pmeas_rpt->mac_addr));
138 
139 	PRINTM(MINFO, "Meas: Rpt:  dlgTkn: %d\n", pmeas_rpt->dialog_token);
140 
141 	PRINTM(MINFO, "Meas: Rpt: rptMode: (%x): Rfs[%c] ICp[%c] Lt[%c]\n",
142 	       *(t_u8 *)&pmeas_rpt->rpt_mode,
143 	       pmeas_rpt->rpt_mode.refused ? 'X' : ' ',
144 	       pmeas_rpt->rpt_mode.incapable ? 'X' : ' ',
145 	       pmeas_rpt->rpt_mode.late ? 'X' : ' ');
146 #ifdef DEBUG_LEVEL2
147 	PRINTM(MINFO, "Meas: Rpt: measTyp: %s\n",
148 	       wlan_meas_get_meas_type_str(pmeas_rpt->meas_type));
149 #endif
150 
151 	type = wlan_le32_to_cpu(pmeas_rpt->meas_type);
152 	switch (type) {
153 	case WLAN_MEAS_BASIC:
154 		PRINTM(MINFO, "Meas: Rpt: chan: %u\n",
155 		       pmeas_rpt->rpt.basic.channel);
156 		PRINTM(MINFO, "Meas: Rpt: strt: %llu\n",
157 		       wlan_le64_to_cpu(pmeas_rpt->rpt.basic.start_time));
158 		PRINTM(MINFO, "Meas: Rpt:  dur: %u\n",
159 		       wlan_le16_to_cpu(pmeas_rpt->rpt.basic.duration));
160 		PRINTM(MINFO, "Meas: Rpt:  bas: (%x): unmsd[%c], radar[%c]\n",
161 		       *(t_u8 *)&(pmeas_rpt->rpt.basic.map),
162 		       pmeas_rpt->rpt.basic.map.unmeasured ? 'X' : ' ',
163 		       pmeas_rpt->rpt.basic.map.radar ? 'X' : ' ');
164 		PRINTM(MINFO, "Meas: Rpt:  bas: unidSig[%c] ofdm[%c] bss[%c]\n",
165 		       pmeas_rpt->rpt.basic.map.unidentified_sig ? 'X' : ' ',
166 		       pmeas_rpt->rpt.basic.map.ofdm_preamble ? 'X' : ' ',
167 		       pmeas_rpt->rpt.basic.map.bss ? 'X' : ' ');
168 		break;
169 	default:
170 		PRINTM(MINFO, "Meas: Rpt: <unhandled>\n");
171 		break;
172 	}
173 
174 	PRINTM(MINFO, "Meas: Rpt: ------------------------------\n");
175 	LEAVE();
176 }
177 
178 /**
179  *  @brief Retrieve a measurement report from the firmware
180  *
181  *  Callback from command processing when a measurement report is received
182  *    from the firmware.  Perform the following when a report is received:
183  *
184  *   -# Debug displays the report if compiled with the appropriate flags
185  *   -# If we are pending on a specific measurement report token, and it
186  *      matches the received report's token, store the report and wake up
187  *      any pending threads
188  *
189  *  @param pmpriv Private driver information structure
190  *  @param resp HostCmd_DS_COMMAND struct returned from the firmware command
191  *              passing a HostCmd_DS_MEASUREMENT_REPORT structure.
192  *
193  *  @return     MLAN_STATUS_SUCCESS
194  */
wlan_meas_cmdresp_get_report(mlan_private * pmpriv,const HostCmd_DS_COMMAND * resp)195 static int wlan_meas_cmdresp_get_report(mlan_private *pmpriv,
196 					const HostCmd_DS_COMMAND *resp)
197 {
198 	mlan_adapter *pmadapter = pmpriv->adapter;
199 	const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt = &resp->params.meas_rpt;
200 
201 	ENTER();
202 
203 	PRINTM(MINFO, "Meas: Rpt: %#x-%u, Seq=%u, Ret=%u\n", resp->command,
204 	       resp->size, resp->seq_num, resp->result);
205 
206 	/* Debug displays the measurement report */
207 	wlan_meas_dump_meas_rpt(pmeas_rpt);
208 
209 	/*
210 	 * Check if we are pending on a measurement report and it matches
211 	 *  the dialog token of the received report:
212 	 */
213 	if (pmadapter->state_meas.meas_rpt_pend_on &&
214 	    pmadapter->state_meas.meas_rpt_pend_on == pmeas_rpt->dialog_token) {
215 		PRINTM(MINFO, "Meas: Rpt: RCV'd Pend on meas #%d\n",
216 		       pmadapter->state_meas.meas_rpt_pend_on);
217 
218 		/* Clear the pending report indicator */
219 		pmadapter->state_meas.meas_rpt_pend_on = 0;
220 
221 		/* Copy the received report into the measurement state for
222 		 * retrieval */
223 		memcpy_ext(pmadapter, &pmadapter->state_meas.meas_rpt_returned,
224 			   pmeas_rpt,
225 			   sizeof(pmadapter->state_meas.meas_rpt_returned),
226 			   sizeof(pmadapter->state_meas.meas_rpt_returned));
227 
228 		/*
229 		 * Wake up any threads pending on the wait queue
230 		 */
231 		wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_MEAS_REPORT, MNULL);
232 	}
233 
234 	LEAVE();
235 
236 	return MLAN_STATUS_SUCCESS;
237 }
238 
239 /**
240  *  @brief Prepare CMD_MEASURMENT_REPORT firmware command
241  *
242  *  @param pmpriv     Private driver information structure
243  *  @param pcmd_ptr   Output parameter: Pointer to the command being prepared
244  *                    for the firmware
245  *  @param pinfo_buf  HostCmd_DS_MEASUREMENT_REQUEST passed as void data block
246  *
247  *  @return          MLAN_STATUS_SUCCESS
248  */
wlan_meas_cmd_request(mlan_private * pmpriv,HostCmd_DS_COMMAND * pcmd_ptr,const void * pinfo_buf)249 static int wlan_meas_cmd_request(mlan_private *pmpriv,
250 				 HostCmd_DS_COMMAND *pcmd_ptr,
251 				 const void *pinfo_buf)
252 {
253 	const HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req =
254 		(HostCmd_DS_MEASUREMENT_REQUEST *)pinfo_buf;
255 
256 	ENTER();
257 
258 	pcmd_ptr->command = HostCmd_CMD_MEASUREMENT_REQUEST;
259 	pcmd_ptr->size = sizeof(HostCmd_DS_MEASUREMENT_REQUEST) + S_DS_GEN;
260 
261 	memcpy_ext(pmpriv->adapter, &pcmd_ptr->params.meas_req, pmeas_req,
262 		   sizeof(pcmd_ptr->params.meas_req),
263 		   sizeof(pcmd_ptr->params.meas_req));
264 
265 	PRINTM(MINFO, "Meas: Req: %#x-%u, Seq=%u, Ret=%u\n", pcmd_ptr->command,
266 	       pcmd_ptr->size, pcmd_ptr->seq_num, pcmd_ptr->result);
267 
268 	wlan_meas_dump_meas_req(pmeas_req);
269 
270 	LEAVE();
271 
272 	return MLAN_STATUS_SUCCESS;
273 }
274 
275 /**
276  *  @brief  Retrieve a measurement report from the firmware
277  *
278  *  The firmware will send a EVENT_MEAS_REPORT_RDY event when it
279  *    completes or receives a measurement report.  The event response
280  *    handler will then start a HostCmd_CMD_MEASUREMENT_REPORT firmware command
281  *    which gets completed for transmission to the firmware in this routine.
282  *
283  *  @param pmpriv    Private driver information structure
284  *  @param pcmd_ptr  Output parameter: Pointer to the command being prepared
285  *                   for the firmware
286  *
287  *  @return        MLAN_STATUS_SUCCESS
288  */
wlan_meas_cmd_get_report(mlan_private * pmpriv,HostCmd_DS_COMMAND * pcmd_ptr)289 static int wlan_meas_cmd_get_report(mlan_private *pmpriv,
290 				    HostCmd_DS_COMMAND *pcmd_ptr)
291 {
292 	ENTER();
293 
294 	pcmd_ptr->command = HostCmd_CMD_MEASUREMENT_REPORT;
295 	pcmd_ptr->size = sizeof(HostCmd_DS_MEASUREMENT_REPORT) + S_DS_GEN;
296 
297 	memset(pmpriv->adapter, &pcmd_ptr->params.meas_rpt, 0x00,
298 	       sizeof(pcmd_ptr->params.meas_rpt));
299 
300 	/*
301 	 * Set the meas_rpt.mac_addr to our mac address to get a meas report,
302 	 *   setting the mac to another STA address instructs the firmware
303 	 *   to transmit this measurement report frame instead
304 	 */
305 	memcpy_ext(pmpriv->adapter, pcmd_ptr->params.meas_rpt.mac_addr,
306 		   pmpriv->curr_addr,
307 		   sizeof(pcmd_ptr->params.meas_rpt.mac_addr),
308 		   sizeof(pcmd_ptr->params.meas_rpt.mac_addr));
309 
310 	LEAVE();
311 
312 	return MLAN_STATUS_SUCCESS;
313 }
314 
315 /********************************************************
316 			Global functions
317 ********************************************************/
318 
319 /**
320  *  @brief Send the input measurement request to the firmware.
321  *
322  *  If the dialog token in the measurement request is set to 0, the function
323  *    will use an local static auto-incremented token in the measurement
324  *    request.  This ensures the dialog token is always set.
325  *
326  *  If wait_for_resp_timeout is set, the function will block its return on
327  *     a timeout or returned measurement report that matches the requests
328  *     dialog token.
329  *
330  *  @param pmpriv                  Private driver information structure
331  *  @param pmeas_req               Pointer to the measurement request to send
332  *  @param wait_for_resp_timeout   Timeout value of the measurement request
333  *                                 in ms.
334  *  @param pioctl_req              Pointer to IOCTL request buffer
335  *  @param pmeas_rpt               Output parameter: Pointer for the resulting
336  *                                 measurement report
337  *
338  *  @return
339  *    - 0 for success
340  *    - -ETIMEDOUT if the measurement report does not return before
341  *      the timeout expires
342  *    - Error return from wlan_prepare_cmd routine otherwise
343  */
wlan_meas_util_send_req(mlan_private * pmpriv,HostCmd_DS_MEASUREMENT_REQUEST * pmeas_req,t_u32 wait_for_resp_timeout,pmlan_ioctl_req pioctl_req,HostCmd_DS_MEASUREMENT_REPORT * pmeas_rpt)344 int wlan_meas_util_send_req(mlan_private *pmpriv,
345 			    HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req,
346 			    t_u32 wait_for_resp_timeout,
347 			    pmlan_ioctl_req pioctl_req,
348 			    HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt)
349 {
350 	static t_u8 auto_dialog_tok;
351 	wlan_meas_state_t *pmeas_state = &pmpriv->adapter->state_meas;
352 	int ret;
353 
354 	ENTER();
355 
356 	/* If dialogTok was set to 0 or not provided, autoset */
357 	pmeas_req->dialog_token =
358 		(pmeas_req->dialog_token ? pmeas_req->dialog_token :
359 					   ++auto_dialog_tok);
360 
361 	/* Check for rollover of the dialog token.  Avoid using 0 as a token */
362 	pmeas_req->dialog_token =
363 		(pmeas_req->dialog_token ? pmeas_req->dialog_token : 1);
364 
365 	/*
366 	 * If the request is to pend waiting for the result, set the dialog
367 	 * token of this measurement request in the state structure.  The
368 	 * measurement report handling routines can then check the incoming
369 	 * measurement reports for a match with this dialog token.
370 	 */
371 	if (wait_for_resp_timeout) {
372 		pmeas_state->meas_rpt_pend_on = pmeas_req->dialog_token;
373 		PRINTM(MINFO, "Meas: Req: START Pend on meas #%d\n",
374 		       pmeas_req->dialog_token);
375 	}
376 
377 	/* Send the measurement request to the firmware */
378 	ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MEASUREMENT_REQUEST,
379 			       HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req,
380 			       (void *)pmeas_req);
381 
382 	LEAVE();
383 	return ret;
384 }
385 
386 /**
387  *  @brief  Prepare the HostCmd_DS_Command structure for a measurement command.
388  *
389  *  Use the Command field to determine if the command being set up is for
390  *     11h and call one of the local command handlers accordingly for:
391  *
392  *        - HostCmd_CMD_MEASUREMENT_REQUEST
393  *        - HostCmd_CMD_MEASUREMENT_REPORT
394  *
395  *  @param pmpriv     Private driver information structure
396  *  @param pcmd_ptr   Output parameter: Pointer to the command being prepared
397  *                    for the firmware
398  *  @param pinfo_buf  Void buffer passthrough with data necessary for a
399  *                    specific command type
400  *
401  *  @return         MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
402  *
403  */
wlan_meas_cmd_process(mlan_private * pmpriv,HostCmd_DS_COMMAND * pcmd_ptr,const void * pinfo_buf)404 int wlan_meas_cmd_process(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd_ptr,
405 			  const void *pinfo_buf)
406 {
407 	int ret = MLAN_STATUS_SUCCESS;
408 
409 	ENTER();
410 	switch (pcmd_ptr->command) {
411 	case HostCmd_CMD_MEASUREMENT_REQUEST:
412 		ret = wlan_meas_cmd_request(pmpriv, pcmd_ptr, pinfo_buf);
413 		break;
414 	case HostCmd_CMD_MEASUREMENT_REPORT:
415 		ret = wlan_meas_cmd_get_report(pmpriv, pcmd_ptr);
416 		break;
417 	default:
418 		ret = MLAN_STATUS_FAILURE;
419 	}
420 
421 	pcmd_ptr->command = wlan_cpu_to_le16(pcmd_ptr->command);
422 	pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size);
423 	LEAVE();
424 	return ret;
425 }
426 
427 /**
428  *  @brief Handle the command response from the firmware for a measurement
429  *         command
430  *
431  *  Use the Command field to determine if the command response being
432  *    is for meas.  Call the local command response handler accordingly for:
433  *
434  *        - HostCmd_CMD_802_MEASUREMENT_REQUEST
435  *        - HostCmd_CMD_802_MEASUREMENT_REPORT
436  *
437  *  @param pmpriv Private driver information structure
438  *  @param resp   HostCmd_DS_COMMAND struct returned from the firmware command
439  *
440  *  @return     MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
441  */
wlan_meas_cmdresp_process(mlan_private * pmpriv,const HostCmd_DS_COMMAND * resp)442 int wlan_meas_cmdresp_process(mlan_private *pmpriv,
443 			      const HostCmd_DS_COMMAND *resp)
444 {
445 	int ret = MLAN_STATUS_SUCCESS;
446 
447 	ENTER();
448 	switch (resp->command) {
449 	case HostCmd_CMD_MEASUREMENT_REQUEST:
450 		PRINTM(MINFO, "Meas: Req Resp: Sz=%u, Seq=%u, Ret=%u\n",
451 		       resp->size, resp->seq_num, resp->result);
452 		break;
453 	case HostCmd_CMD_MEASUREMENT_REPORT:
454 		ret = wlan_meas_cmdresp_get_report(pmpriv, resp);
455 		break;
456 	default:
457 		ret = MLAN_STATUS_FAILURE;
458 	}
459 
460 	LEAVE();
461 	return ret;
462 }
463