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