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