blob: 292ac6752a56cc119fb900fa7e5fa3a7154cd41b [file] [log] [blame]
/**
* @file mlan_meas.c
*
* @brief Implementation of measurement interface code with the app/firmware
*
* Driver implementation for sending and retrieving measurement requests
* and responses.
*
* Current use is limited to 802.11h.
*
* Requires use of the following preprocessor define:
* - ENABLE_MEAS
*
* Copyright (C) 2008-2009, Marvell International Ltd.
* All Rights Reserved
*
*/
/*************************************************************
Change Log:
03/24/2009: initial version
************************************************************/
#include "mlan.h"
#include "mlan_join.h"
#include "mlan_util.h"
#include "mlan_fw.h"
#include "mlan_main.h"
#include "mlan_ioctl.h"
#include "mlan_meas.h"
/** Default measurement duration when not provided by the application */
#define WLAN_MEAS_DEFAULT_MEAS_DURATION 1000U /* TUs */
#ifdef DEBUG_LEVEL2
/** String descriptions of the different measurement enums. Debug display */
static const char *meas_type_str[WLAN_MEAS_NUM_TYPES] = {
"basic",
};
/**
* @brief Retrieve the measurement string representation of a meas_type enum
* Used for debug display only
*
* @param meas_type Measurement type enumeration input for string lookup
*
* @return Constant string representing measurement type
*/
static const char *
wlan_meas_get_meas_type_str(MeasType_t meas_type)
{
if (meas_type <= WLAN_MEAS_11H_MAX_TYPE)
return meas_type_str[meas_type];
return "Invld";
}
#endif
/**
* @brief Debug print display of the input measurement request
*
* @param pmeas_req Pointer to the measurement request to display
*
* @return void
*/
static void
wlan_meas_dump_meas_req(const HostCmd_DS_MEASUREMENT_REQUEST * pmeas_req)
{
PRINTM(MINFO, "Meas: Req: ------------------------------\n");
PRINTM(MINFO, "Meas: Req: mac_addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
pmeas_req->mac_addr[0],
pmeas_req->mac_addr[1],
pmeas_req->mac_addr[2],
pmeas_req->mac_addr[3],
pmeas_req->mac_addr[4], pmeas_req->mac_addr[5]);
PRINTM(MINFO, "Meas: Req: dlgTkn: %d\n", pmeas_req->dialog_token);
PRINTM(MINFO, "Meas: Req: mode: dm[%c] rpt[%c] req[%c]\n",
pmeas_req->req_mode.duration_mandatory ? 'X' : ' ',
pmeas_req->req_mode.report ? 'X' : ' ',
pmeas_req->req_mode.request ? 'X' : ' ');
PRINTM(MINFO, "Meas: Req: : en[%c] par[%c]\n",
pmeas_req->req_mode.enable ? 'X' : ' ',
pmeas_req->req_mode.parallel ? 'X' : ' ');
#ifdef DEBUG_LEVEL2
PRINTM(MINFO, "Meas: Req: measTyp: %s\n",
wlan_meas_get_meas_type_str(pmeas_req->meas_type));
#endif
switch (pmeas_req->meas_type) {
case WLAN_MEAS_BASIC:
/* Lazy cheat, fields of bas, cca, rpi union match on the request */
PRINTM(MINFO, "Meas: Req: chan: %u\n", pmeas_req->req.basic.channel);
PRINTM(MINFO, "Meas: Req: strt: %llu\n",
wlan_le64_to_cpu(pmeas_req->req.basic.start_time));
PRINTM(MINFO, "Meas: Req: dur: %u\n",
wlan_le16_to_cpu(pmeas_req->req.basic.duration));
break;
default:
PRINTM(MINFO, "Meas: Req: <unhandled>\n");
break;
}
PRINTM(MINFO, "Meas: Req: ------------------------------\n");
}
/**
* @brief Debug print display of the input measurement report
*
* @param pmeas_rpt Pointer to measurement report to display
*
* @return void
*/
static void
wlan_meas_dump_meas_rpt(const HostCmd_DS_MEASUREMENT_REPORT * pmeas_rpt)
{
PRINTM(MINFO, "Meas: Rpt: ------------------------------\n");
PRINTM(MINFO, "Meas: Rpt: mac_addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
pmeas_rpt->mac_addr[0],
pmeas_rpt->mac_addr[1],
pmeas_rpt->mac_addr[2],
pmeas_rpt->mac_addr[3],
pmeas_rpt->mac_addr[4], pmeas_rpt->mac_addr[5]);
PRINTM(MINFO, "Meas: Rpt: dlgTkn: %d\n", pmeas_rpt->dialog_token);
PRINTM(MINFO, "Meas: Rpt: rptMode: (%x): Rfs[%c] ICp[%c] Lt[%c]\n",
*(t_u8 *) & pmeas_rpt->rpt_mode,
pmeas_rpt->rpt_mode.refused ? 'X' : ' ',
pmeas_rpt->rpt_mode.incapable ? 'X' : ' ',
pmeas_rpt->rpt_mode.late ? 'X' : ' ');
#ifdef DEBUG_LEVEL2
PRINTM(MINFO, "Meas: Rpt: measTyp: %s\n",
wlan_meas_get_meas_type_str(pmeas_rpt->meas_type));
#endif
switch (pmeas_rpt->meas_type) {
case WLAN_MEAS_BASIC:
PRINTM(MINFO, "Meas: Rpt: chan: %u\n", pmeas_rpt->rpt.basic.channel);
PRINTM(MINFO, "Meas: Rpt: strt: %llu\n",
wlan_le64_to_cpu(pmeas_rpt->rpt.basic.start_time));
PRINTM(MINFO, "Meas: Rpt: dur: %u\n",
wlan_le16_to_cpu(pmeas_rpt->rpt.basic.duration));
PRINTM(MINFO, "Meas: Rpt: bas: (%x): unmsd[%c], radar[%c]\n",
*(t_u8 *) & (pmeas_rpt->rpt.basic.map),
pmeas_rpt->rpt.basic.map.unmeasured ? 'X' : ' ',
pmeas_rpt->rpt.basic.map.radar ? 'X' : ' ');
PRINTM(MINFO, "Meas: Rpt: bas: unidSig[%c] ofdm[%c] bss[%c]\n",
pmeas_rpt->rpt.basic.map.unidentified_sig ? 'X' : ' ',
pmeas_rpt->rpt.basic.map.ofdm_preamble ? 'X' : ' ',
pmeas_rpt->rpt.basic.map.bss ? 'X' : ' ');
break;
default:
PRINTM(MINFO, "Meas: Rpt: <unhandled>\n");
break;
}
PRINTM(MINFO, "Meas: Rpt: ------------------------------\n");
}
/**
* @brief Retrieve a measurement report from the firmware
*
* Callback from command processing when a measurement report is received
* from the firmware. Perform the following when a report is received:
*
* -# Debug displays the report if compiled with the appropriate flags
* -# If we are pending on a specific measurement report token, and it
* matches the received report's token, store the report and wake up
* any pending threads
*
* @param pmpriv Private driver information structure
* @param resp HostCmd_DS_COMMAND struct returned from the firmware command
* passing a HostCmd_DS_MEASUREMENT_REPORT structure.
*
* @return MLAN_STATUS_SUCCESS
*/
static int
wlan_meas_cmdresp_get_report(mlan_private * pmpriv,
const HostCmd_DS_COMMAND * resp)
{
mlan_adapter *pmadapter = pmpriv->adapter;
const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt = &resp->params.meas_rpt;
ENTER();
PRINTM(MINFO, "Meas: Rpt: %#x-%u, Seq=%u, Ret=%u\n",
resp->command, resp->size, resp->seq_num, resp->result);
/* Debug displays the measurement report */
wlan_meas_dump_meas_rpt(pmeas_rpt);
/*
* Check if we are pending on a measurement report and it matches
* the dialog token of the received report:
*/
if (pmadapter->state_meas.meas_rpt_pend_on
&& pmadapter->state_meas.meas_rpt_pend_on == pmeas_rpt->dialog_token) {
PRINTM(MINFO, "Meas: Rpt: RCV'd Pend on meas #%d\n",
pmadapter->state_meas.meas_rpt_pend_on);
/* Clear the pending report indicator */
pmadapter->state_meas.meas_rpt_pend_on = 0;
/* Copy the received report into the measurement state for retrieval */
memcpy(&pmadapter->state_meas.meas_rpt_returned, pmeas_rpt,
sizeof(pmadapter->state_meas.meas_rpt_returned));
/*
* Wake up any threads pending on the wait queue
*/
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Prepare CMD_MEASURMENT_REPORT firmware command
*
* @param pmpriv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being prepared
* for the firmware
* @param pinfo_buf HostCmd_DS_MEASUREMENT_REQUEST passed as void data block
*
* @return MLAN_STATUS_SUCCESS
*/
static int
wlan_meas_cmd_request(mlan_private * pmpriv,
HostCmd_DS_COMMAND * pcmd_ptr, const void *pinfo_buf)
{
const HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req =
(HostCmd_DS_MEASUREMENT_REQUEST *) pinfo_buf;
ENTER();
pcmd_ptr->command = HostCmd_CMD_MEASUREMENT_REQUEST;
pcmd_ptr->size = sizeof(HostCmd_DS_MEASUREMENT_REQUEST) + S_DS_GEN;
memcpy(&pcmd_ptr->params.meas_req, pmeas_req,
sizeof(pcmd_ptr->params.meas_req));
PRINTM(MINFO, "Meas: Req: %#x-%u, Seq=%u, Ret=%u\n",
pcmd_ptr->command, pcmd_ptr->size, pcmd_ptr->seq_num,
pcmd_ptr->result);
wlan_meas_dump_meas_req(pmeas_req);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Retrieve a measurement report from the firmware
*
* The firmware will send a EVENT_MEAS_REPORT_RDY event when it
* completes or receives a measurement report. The event response
* handler will then start a HostCmd_CMD_MEASUREMENT_REPORT firmware command
* which gets completed for transmission to the firmware in this routine.
*
* @param pmpriv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being prepared
* for the firmware
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static int
wlan_meas_cmd_get_report(mlan_private * pmpriv, HostCmd_DS_COMMAND * pcmd_ptr)
{
ENTER();
pcmd_ptr->command = HostCmd_CMD_MEASUREMENT_REPORT;
pcmd_ptr->size = sizeof(HostCmd_DS_MEASUREMENT_REPORT) + S_DS_GEN;
memset(&pcmd_ptr->params.meas_rpt, 0x00, sizeof(pcmd_ptr->params.meas_rpt));
/*
* Set the meas_rpt.mac_addr to our mac address to get a meas report,
* setting the mac to another STA address instructs the firmware
* to transmit this measurement report frame instead
*/
memcpy(pcmd_ptr->params.meas_rpt.mac_addr, pmpriv->curr_addr,
sizeof(pcmd_ptr->params.meas_rpt.mac_addr));
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Initialize any needed structures for the measurement code
*
* @param pmadapter mlan_adapter structure
*
* @return void
*/
void
wlan_meas_init(mlan_adapter * pmadapter)
{
ENTER();
LEAVE();
}
/**
* @brief Send the input measurement request to the firmware.
*
* If the dialog token in the measurement request is set to 0, the function
* will use an local static auto-incremented token in the measurement
* request. This ensures the dialog token is always set.
*
* If wait_for_resp_timeout is set, the function will block its return on
* a timeout or returned measurement report that matches the requests
* dialog token.
*
* @param pmpriv Private driver information structure
* @param pmeas_req Pointer to the measurement request to send
* @param wait_for_resp_timeout Timeout value of the measurement request
* in ms.
* @param pmeas_rpt Output parameter: Pointer for the resulting
* measurement report
*
* @return
* - 0 for success
* - -ETIMEDOUT if the measurement report does not return before
* the timeout expires
* - Error return from wlan_prepare_cmd routine otherwise
*/
int
wlan_meas_util_send_req(mlan_private * pmpriv,
HostCmd_DS_MEASUREMENT_REQUEST * pmeas_req,
t_u32 wait_for_resp_timeout,
HostCmd_DS_MEASUREMENT_REPORT * pmeas_rpt)
{
static t_u8 auto_dialog_tok = 0;
wlan_meas_state_t *pmeas_state = &pmpriv->adapter->state_meas;
int ret;
t_u32 calc_timeout;
ENTER();
/* If dialogTok was set to 0 or not provided, autoset */
pmeas_req->dialog_token = (pmeas_req->dialog_token ?
pmeas_req->dialog_token : ++auto_dialog_tok);
/* Check for rollover of the dialog token. Avoid using 0 as a token */
pmeas_req->dialog_token = (pmeas_req->dialog_token ?
pmeas_req->dialog_token : 1);
/*
* If the request is to pend waiting for the result, set the dialog token
* of this measurement request in the state structure. The measurement
* report handling routines can then check the incoming measurement
* reports for a match with this dialog token.
*/
if (wait_for_resp_timeout) {
pmeas_state->meas_rpt_pend_on = pmeas_req->dialog_token;
PRINTM(MINFO, "Meas: Req: START Pend on meas #%d\n",
pmeas_req->dialog_token);
}
/* Send the measurement request to the firmware */
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MEASUREMENT_REQUEST,
HostCmd_ACT_GEN_SET, 0, MNULL, (void *) pmeas_req);
/*
* If the measurement request was sent successfully, and the function
* must wait for the report, suspend execution until the meas_rpt_pend_on
* variable in the state structure has been reset to 0 by the report
* handling routines.
*/
if (!ret && wait_for_resp_timeout) {
/* Add ~25% overhead to the timeout for firmware overhead */
calc_timeout = wait_for_resp_timeout + (wait_for_resp_timeout >> 2);
PRINTM(MINFO, "Meas: Req: TIMEOUT set to %d ms\n", calc_timeout);
/* extra 10 ms for the driver overhead - helps with small meas */
calc_timeout += 10;
PRINTM(MINFO, "Meas: Req: TIMEOUT set to %d milliseconds\n",
calc_timeout);
if (pmeas_state->meas_rpt_pend_on) {
PRINTM(MINFO, "Meas: Req: TIMEOUT Pend on meas #%d\n",
pmeas_req->dialog_token);
ret = MLAN_STATUS_FAILURE;
} else {
PRINTM(MINFO, "Meas: Req: DONE Pend on meas #%d\n",
pmeas_req->dialog_token);
memcpy(pmeas_rpt, &pmeas_state->meas_rpt_returned,
sizeof(HostCmd_DS_MEASUREMENT_REPORT));
}
}
/*
* The measurement request failed or we are not waiting for a response.
* In either case, the rpt_pend_on variable should be zero.
*/
pmeas_state->meas_rpt_pend_on = 0;
LEAVE();
return ret;
}
/**
* @brief Prepare the HostCmd_DS_Command structure for a measurement command.
*
* Use the Command field to determine if the command being set up is for
* 11h and call one of the local command handlers accordingly for:
*
* - HostCmd_CMD_MEASUREMENT_REQUEST
* - HostCmd_CMD_MEASUREMENT_REPORT
*
* @param pmpriv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being prepared
* for the firmware
* @param pinfo_buf Void buffer passthrough with data necessary for a
* specific command type
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*
*/
int
wlan_meas_cmd_process(mlan_private * pmpriv,
HostCmd_DS_COMMAND * pcmd_ptr, const void *pinfo_buf)
{
int ret = MLAN_STATUS_SUCCESS;
ENTER();
switch (pcmd_ptr->command) {
case HostCmd_CMD_MEASUREMENT_REQUEST:
ret = wlan_meas_cmd_request(pmpriv, pcmd_ptr, pinfo_buf);
break;
case HostCmd_CMD_MEASUREMENT_REPORT:
ret = wlan_meas_cmd_get_report(pmpriv, pcmd_ptr);
break;
default:
ret = MLAN_STATUS_FAILURE;
}
pcmd_ptr->command = wlan_cpu_to_le16(pcmd_ptr->command);
pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size);
LEAVE();
return ret;
}
/**
* @brief Handle the command response from the firmware for a measurement
* command
*
* Use the Command field to determine if the command response being
* is for meas. Call the local command response handler accordingly for:
*
* - HostCmd_CMD_802_MEASUREMENT_REQUEST
* - HostCmd_CMD_802_MEASUREMENT_REPORT
*
* @param pmpriv Private driver information structure
* @param resp HostCmd_DS_COMMAND struct returned from the firmware command
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
int
wlan_meas_cmdresp_process(mlan_private * pmpriv,
const HostCmd_DS_COMMAND * resp)
{
int ret = MLAN_STATUS_SUCCESS;
ENTER();
switch (resp->command) {
case HostCmd_CMD_MEASUREMENT_REQUEST:
PRINTM(MINFO, "Meas: Req Resp: Sz=%u, Seq=%u, Ret=%u\n",
resp->size, resp->seq_num, resp->result);
break;
case HostCmd_CMD_MEASUREMENT_REPORT:
ret = wlan_meas_cmdresp_get_report(pmpriv, resp);
break;
default:
ret = MLAN_STATUS_FAILURE;
}
LEAVE();
return ret;
}