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