| /** @file mlan_11h.c |
| * |
| * @brief This file contains functions for 802.11H. |
| * |
| * Copyright (C) 2008-2009, Marvell International Ltd. |
| * All Rights Reserved |
| */ |
| |
| /************************************************************* |
| Change Log: |
| 03/26/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" |
| #include "mlan_11h.h" |
| |
| /* |
| * Constants |
| */ |
| |
| /** Default IBSS DFS recovery interval (in TBTTs); used for adhoc start */ |
| #define WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL 100 |
| |
| /** Default 11h power constraint used to offset the maximum transmit power */ |
| #define WLAN_11H_TPC_POWERCONSTRAINT 0 |
| |
| /** 11h TPC Power capability minimum setting, sent in TPC_INFO command to fw */ |
| #define WLAN_11H_TPC_POWERCAPABILITY_MIN 5 |
| |
| /** 11h TPC Power capability maximum setting, sent in TPC_INFO command to fw */ |
| #define WLAN_11H_TPC_POWERCAPABILITY_MAX 20 |
| |
| /** Regulatory requirement for the duration of a channel availability check */ |
| #define WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION 60000 /* in ms */ |
| |
| /** U-NII sub-band config : Start Channel = 36, NumChans = 4 */ |
| static const |
| IEEEtypes_SupportChan_Subband_t wlan_11h_unii_lower_band = { 36, 4 }; |
| |
| /** U-NII sub-band config : Start Channel = 52, NumChans = 4 */ |
| static const |
| IEEEtypes_SupportChan_Subband_t wlan_11h_unii_middle_band = { 52, 4 }; |
| |
| /** U-NII sub-band config : Start Channel = 100, NumChans = 11 */ |
| static const |
| IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band = { 100, 11 }; |
| |
| /** U-NII sub-band config : Start Channel = 149, NumChans = 4 */ |
| static const |
| IEEEtypes_SupportChan_Subband_t wlan_11h_unii_upper_band = { 149, 4 }; |
| |
| /* |
| * Local types |
| */ |
| |
| /** Internally passed structure used to send a CMD_802_11_TPC_INFO command */ |
| typedef struct |
| { |
| t_u8 chan; /**< Channel to which the power constraint applies */ |
| t_u8 power_constraint; /**< Local power constraint to send to firmware */ |
| } wlan_11h_tpc_info_param_t; |
| |
| /** |
| * @brief Utility function to get a random number based on the underlying OS |
| * |
| * @return random integer |
| */ |
| static int |
| wlan_11h_get_random_num(t_void) |
| { |
| return 112233; |
| |
| } |
| |
| /** |
| * @brief Convert an IEEE formatted IE to 16-bit ID/Len Marvell |
| * proprietary format |
| * |
| * @param pout_buf Output parameter: Buffer to output Marvell formatted IE |
| * @param pin_ie Pointer to IEEE IE to be converted to Marvell format |
| * |
| * @return Number of bytes output to pout_buf parameter return |
| */ |
| static int |
| wlan_11h_convert_ieee_to_mrvl_ie(char *pout_buf, const char *pin_ie) |
| { |
| MrvlIEtypesHeader_t mrvl_ie_hdr; |
| char *ptmp_buf = pout_buf; |
| |
| ENTER(); |
| /* Assign the Element Id and Len to the Marvell struct attributes */ |
| mrvl_ie_hdr.type = wlan_cpu_to_le16(pin_ie[0]); |
| mrvl_ie_hdr.len = wlan_cpu_to_le16(pin_ie[1]); |
| |
| /* If the element ID is zero, return without doing any copying */ |
| if (!mrvl_ie_hdr.type) |
| return 0; |
| |
| /* Copy the header to the buffer pointer */ |
| memcpy(ptmp_buf, &mrvl_ie_hdr, sizeof(mrvl_ie_hdr)); |
| |
| /* Increment the temp buffer pointer by the size appended */ |
| ptmp_buf += sizeof(mrvl_ie_hdr); |
| |
| /* Append the data section of the IE; length given by the IEEE IE length */ |
| memcpy(ptmp_buf, pin_ie + 2, pin_ie[1]); |
| |
| LEAVE(); |
| /* Return the number of bytes appended to pout_buf */ |
| return (sizeof(mrvl_ie_hdr) + pin_ie[1]); |
| } |
| |
| /** |
| * @brief Setup the IBSS DFS element passed to the firmware in adhoc start |
| * and join commands |
| * |
| * The DFS Owner and recovery fields are set to be our MAC address and |
| * a predetermined constant recovery value. If we are joining an adhoc |
| * network, these values are replaced with the existing IBSS values. |
| * They are valid only when starting a new IBSS. |
| * |
| * The IBSS DFS Element is variable in size based on the number of |
| * channels supported in our current region. |
| * |
| * @param priv Private driver information structure |
| * @param pdfs Output parameter: Pointer to the IBSS DFS element setup by |
| * this function. |
| * |
| * @return |
| * - Length of the returned element in pdfs output parameter |
| * - 0 if returned element is not setup |
| */ |
| static int |
| wlan_11h_set_ibss_dfs_ie(mlan_private * priv, IEEEtypes_IBSS_DFS_t * pdfs) |
| { |
| int num_chans = 0; |
| MeasRptBasicMap_t initial_map; |
| mlan_adapter *adapter = priv->adapter; |
| |
| ENTER(); |
| PRINTM(MINFO, "11h: IBSS DFS Element, 11D parsed region: %c%c (0x%x)\n", |
| adapter->parsed_region_chan.country_code[0], |
| adapter->parsed_region_chan.country_code[1], |
| adapter->parsed_region_chan.region); |
| |
| memset(pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t)); |
| |
| /* |
| * A basic measurement report is included with each channel in the |
| * map field. Initial value for the map for each supported channel |
| * is with only the unmeasured bit set. |
| */ |
| memset(&initial_map, 0x00, sizeof(initial_map)); |
| initial_map.unmeasured = 1; |
| |
| /* Set the DFS Owner and recovery interval fields */ |
| memcpy(pdfs->dfs_owner, priv->curr_addr, sizeof(pdfs->dfs_owner)); |
| pdfs->dfs_recovery_interval = WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL; |
| |
| for (; (num_chans < adapter->parsed_region_chan.no_of_chan) |
| && (num_chans < WLAN_11H_MAX_IBSS_DFS_CHANNELS); num_chans++) { |
| pdfs->channel_map[num_chans].channel_number = |
| adapter->parsed_region_chan.chan_pwr[num_chans].chan; |
| |
| /* |
| * Set the inital map field with a basic measurement |
| */ |
| pdfs->channel_map[num_chans].rpt_map = initial_map; |
| } |
| |
| /* |
| * If we have an established channel map, include it and return |
| * a valid DFS element |
| */ |
| if (num_chans) { |
| PRINTM(MINFO, "11h: Added %d channels to IBSS DFS Map\n", num_chans); |
| |
| pdfs->element_id = IBSS_DFS; |
| pdfs->len = |
| (sizeof(pdfs->dfs_owner) + sizeof(pdfs->dfs_recovery_interval) |
| + num_chans * sizeof(IEEEtypes_ChannelMap_t)); |
| |
| return (pdfs->len + sizeof(pdfs->len) + sizeof(pdfs->element_id)); |
| } |
| |
| /* Ensure the element is zeroed out for an invalid return */ |
| memset(pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t)); |
| |
| LEAVE(); |
| return 0; |
| } |
| |
| /** |
| * @brief Setup the Supported Channel IE sent in association requests |
| * |
| * The Supported Channels IE is required to be sent when the spectrum |
| * management capability (11h) is enabled. The element contains a |
| * starting channel and number of channels tuple for each sub-band |
| * the STA supports. This information is based on the operating region. |
| * |
| * @param priv Private driver information structure |
| * @param psup_chan Output parameter: Pointer to the Supported Chan element |
| * setup by this function. |
| * |
| * @return |
| * - Length of the returned element in psup_chan output parameter |
| * - 0 if returned element is not setup |
| */ |
| static int |
| wlan_11h_set_supp_channels_ie(mlan_private * priv, |
| IEEEtypes_SupportedChannels_t * psup_chan) |
| { |
| int num_subbands = 0; |
| int ret_len = 0; |
| |
| ENTER(); |
| memset(psup_chan, 0x00, sizeof(IEEEtypes_SupportedChannels_t)); |
| |
| /* |
| * Set the supported channel elements based on the region code, |
| * incrementing num_subbands for each sub-band we append to the |
| * element. |
| */ |
| switch (priv->adapter->region_code) { |
| case 0x10: /* USA FCC */ |
| case 0x20: /* Canada IC */ |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_lower_band; |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_middle_band; |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_mid_upper_band; |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_upper_band; |
| break; |
| case 0x30: /* Europe ETSI */ |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_lower_band; |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_middle_band; |
| psup_chan->subband[num_subbands++] = wlan_11h_unii_mid_upper_band; |
| break; |
| default: |
| break; |
| } |
| |
| /* |
| * If we have setup any supported subbands in the element, return a |
| * valid IE along with its size, else return 0. |
| */ |
| if (num_subbands) { |
| psup_chan->element_id = SUPPORTED_CHANNELS; |
| psup_chan->len = num_subbands * sizeof(IEEEtypes_SupportChan_Subband_t); |
| |
| ret_len = (psup_chan->len |
| + sizeof(psup_chan->len) + sizeof(psup_chan->element_id)); |
| |
| HEXDUMP("11h: SupChan", (t_u8 *) psup_chan, ret_len); |
| } |
| |
| LEAVE(); |
| return ret_len; |
| } |
| |
| /** |
| * @brief Prepare CMD_802_11_TPC_ADAPT_REQ firmware command |
| * |
| * @param priv Private driver information structure |
| * @param pcmd_ptr Output parameter: Pointer to the command being prepared |
| * for the firmware |
| * @param pinfo_buf HostCmd_DS_802_11_TPC_ADAPT_REQ passed as void data block |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static int |
| wlan_11h_cmd_tpc_request(mlan_private * priv, |
| HostCmd_DS_COMMAND * pcmd_ptr, |
| const t_void * pinfo_buf) |
| { |
| ENTER(); |
| |
| memcpy(&pcmd_ptr->params.tpc_req, pinfo_buf, |
| sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ)); |
| |
| pcmd_ptr->params.tpc_req.req.timeout = |
| wlan_cpu_to_le16(pcmd_ptr->params.tpc_req.req.timeout); |
| |
| /* Converted to little endian in wlan_11h_cmd_process */ |
| pcmd_ptr->size = sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ) + S_DS_GEN; |
| |
| HEXDUMP("11h: 11_TPC_ADAPT_REQ:", (t_u8 *) pcmd_ptr, (int) pcmd_ptr->size); |
| |
| LEAVE(); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Prepare CMD_802_11_TPC_INFO firmware command |
| * |
| * @param priv Private driver information structure |
| * @param pcmd_ptr Output parameter: Pointer to the command being prepared |
| * for the firmware |
| * @param pinfo_buf wlan_11h_tpc_info_param_t passed as void data block |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static int |
| wlan_11h_cmd_tpc_info(mlan_private * priv, |
| HostCmd_DS_COMMAND * pcmd_ptr, const t_void * pinfo_buf) |
| { |
| HostCmd_DS_802_11_TPC_INFO *ptpc_info = &pcmd_ptr->params.tpc_info; |
| MrvlIEtypes_LocalPowerConstraint_t *pconstraint = |
| &ptpc_info->local_constraint; |
| MrvlIEtypes_PowerCapability_t *pcap = &ptpc_info->power_cap; |
| |
| wlan_11h_state_t *pstate = &priv->adapter->state_11h; |
| const wlan_11h_tpc_info_param_t *ptpc_info_param = |
| (wlan_11h_tpc_info_param_t *) pinfo_buf; |
| |
| ENTER(); |
| |
| pcap->min_power = pstate->min_tx_power_capability; |
| pcap->max_power = pstate->max_tx_power_capability; |
| pcap->header.len = wlan_cpu_to_le16(2); |
| pcap->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CAPABILITY); |
| |
| pconstraint->chan = ptpc_info_param->chan; |
| pconstraint->constraint = ptpc_info_param->power_constraint; |
| pconstraint->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CONSTRAINT); |
| pconstraint->header.len = wlan_cpu_to_le16(2); |
| |
| /* Converted to little endian in wlan_11h_cmd_process */ |
| pcmd_ptr->size = sizeof(HostCmd_DS_802_11_TPC_INFO) + S_DS_GEN; |
| |
| HEXDUMP("11h: TPC INFO", (t_u8 *) pcmd_ptr, (int) pcmd_ptr->size); |
| |
| LEAVE(); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Prepare CMD_802_11_CHAN_SW_ANN firmware command |
| * |
| * @param priv Private driver information structure |
| * @param pcmd_ptr Output parameter: Pointer to the command being |
| * prepared to for firmware |
| * @param pinfo_buf HostCmd_DS_802_11_CHAN_SW_ANN passed as void data block |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static int |
| wlan_11h_cmd_chan_sw_ann(mlan_private * priv, |
| HostCmd_DS_COMMAND * pcmd_ptr, |
| const t_void * pinfo_buf) |
| { |
| const HostCmd_DS_802_11_CHAN_SW_ANN *pch_sw_ann = |
| (HostCmd_DS_802_11_CHAN_SW_ANN *) pinfo_buf; |
| |
| ENTER(); |
| |
| /* Converted to little endian in wlan_11h_cmd_process */ |
| pcmd_ptr->size = sizeof(HostCmd_DS_802_11_CHAN_SW_ANN) + S_DS_GEN; |
| |
| memcpy(&pcmd_ptr->params.chan_sw_ann, pch_sw_ann, |
| sizeof(HostCmd_DS_802_11_CHAN_SW_ANN)); |
| |
| PRINTM(MINFO, "11h: ChSwAnn: %#x-%u, Seq=%u, Ret=%u\n", |
| pcmd_ptr->command, pcmd_ptr->size, pcmd_ptr->seq_num, |
| pcmd_ptr->result); |
| PRINTM(MINFO, "11h: ChSwAnn: Ch=%d, Cnt=%d, Mode=%d\n", |
| pch_sw_ann->new_chan, pch_sw_ann->switch_count, |
| pch_sw_ann->switch_mode); |
| |
| LEAVE(); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Set the local power constraint in the firmware |
| * |
| * Construct and send a CMD_802_11_TPC_INFO command with the local power |
| * constraint. |
| * |
| * @param priv Private driver information structure |
| * @param channel Channel to which the power constraint applies |
| * @param power_constraint Power constraint to be applied on the channel |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static int |
| wlan_11h_set_local_power_constraint(mlan_private * priv, |
| t_u8 channel, t_u8 power_constraint) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| wlan_11h_tpc_info_param_t tpc_info_param; |
| |
| ENTER(); |
| tpc_info_param.chan = channel; |
| tpc_info_param.power_constraint = power_constraint; |
| |
| PRINTM(MINFO, "11h: Set Local Constraint = %d\n", |
| tpc_info_param.power_constraint); |
| |
| ret = wlan_prepare_cmd(priv, HostCmd_CMD_802_11_TPC_INFO, |
| HostCmd_ACT_GEN_SET, 0, 0, &tpc_info_param); |
| |
| if (ret) { |
| PRINTM(MINFO, "11h: Err: Send TPC_INFO CMD: %d\n", ret); |
| ret = MLAN_STATUS_FAILURE; |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Utility function to process a join to an infrastructure BSS |
| * |
| * @param priv Private driver information structure |
| * @param ppbuffer Output parameter: Pointer to the TLV output buffer, |
| * modified on return to point after the appended 11h TLVs |
| * @param channel Channel on which we are joining the BSS |
| * @param p11h_bss_info Pointer to the 11h BSS information for this network |
| * that was parsed out of the scan response. |
| * |
| * @return Integer number of bytes appended to the TLV output |
| * buffer (ppbuffer) |
| */ |
| static int |
| wlan_11h_process_infra_join(mlan_private * priv, |
| t_u8 ** ppbuffer, |
| t_u32 channel, wlan_11h_bss_info_t * p11h_bss_info) |
| { |
| MrvlIEtypesHeader_t ie_header; |
| IEEEtypes_SupportedChannels_t sup_chan_ie; |
| int ret_len = 0; |
| int sup_chan_len = 0; |
| |
| ENTER(); |
| |
| /* Null Checks */ |
| if (!ppbuffer) |
| return 0; |
| if (!(*ppbuffer)) |
| return 0; |
| |
| /* Set the local constraint configured in the firmware */ |
| wlan_11h_set_local_power_constraint(priv, channel, |
| (p11h_bss_info-> |
| power_constraint.local_constraint)); |
| |
| /* Setup the Supported Channels IE */ |
| sup_chan_len = wlan_11h_set_supp_channels_ie(priv, &sup_chan_ie); |
| |
| /* |
| * If we returned a valid Supported Channels IE, wrap and append it |
| */ |
| if (sup_chan_len) { |
| /* Wrap the supported channels IE with a passthrough TLV type */ |
| ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); |
| ie_header.len = sup_chan_len; |
| memcpy(*ppbuffer, &ie_header, sizeof(ie_header)); |
| |
| /* Increment the return size and the return buffer pointer param */ |
| *ppbuffer += sizeof(ie_header); |
| ret_len += sizeof(ie_header); |
| |
| /* Copy the supported channels IE to the output buf, advance pointer */ |
| memcpy(*ppbuffer, &sup_chan_ie, sup_chan_len); |
| *ppbuffer += sup_chan_len; |
| ret_len += sup_chan_len; |
| } |
| |
| LEAVE(); |
| return ret_len; |
| } |
| |
| /** |
| * @brief Utility function to process a start or join to an adhoc network |
| * |
| * Add the elements to the TLV buffer needed in the start/join adhoc commands: |
| * - IBSS DFS IE |
| * - Quiet IE |
| * |
| * Also send the local constraint to the firmware in a TPC_INFO command. |
| * |
| * @param priv Private driver information structure |
| * @param ppbuffer Output parameter: Pointer to the TLV output buffer, |
| * modified on return to point after the appended 11h TLVs |
| * @param channel Channel on which we are starting/joining the IBSS |
| * @param p11h_bss_info Pointer to the 11h BSS information for this network |
| * that was parsed out of the scan response. NULL |
| * indicates we are starting the adhoc network |
| * |
| * @return Integer number of bytes appended to the TLV output |
| * buffer (ppbuffer) |
| */ |
| static int |
| wlan_11h_process_adhoc(mlan_private * priv, |
| t_u8 ** ppbuffer, |
| t_u32 channel, wlan_11h_bss_info_t * p11h_bss_info) |
| { |
| IEEEtypes_IBSS_DFS_t dfs_elem; |
| int size_appended; |
| int ret_len = 0; |
| t_s8 local_constraint = 0; |
| mlan_adapter *adapter = priv->adapter; |
| |
| ENTER(); |
| |
| /* Format our own IBSS DFS Element. Include our channel map fields */ |
| wlan_11h_set_ibss_dfs_ie(priv, &dfs_elem); |
| |
| if (p11h_bss_info) { |
| /* |
| * Copy the DFS Owner/Recovery Interval from the BSS we are joining |
| */ |
| memcpy(dfs_elem.dfs_owner, |
| p11h_bss_info->ibss_dfs.dfs_owner, sizeof(dfs_elem.dfs_owner)); |
| dfs_elem.dfs_recovery_interval = |
| p11h_bss_info->ibss_dfs.dfs_recovery_interval; |
| } |
| |
| /* Append the dfs element to the TLV buffer */ |
| size_appended = wlan_11h_convert_ieee_to_mrvl_ie((char *) *ppbuffer, |
| (char *) &dfs_elem); |
| |
| HEXDUMP("11h: IBSS-DFS", (t_u8 *) * ppbuffer, size_appended); |
| *ppbuffer += size_appended; |
| ret_len += size_appended; |
| |
| /* |
| * Check to see if we are joining a network. Join is indicated by the |
| * BSS Info pointer being valid (not NULL) |
| */ |
| if (p11h_bss_info) { |
| /* |
| * If there was a quiet element, include it in adhoc join command |
| */ |
| if (p11h_bss_info->quiet.element_id == QUIET) { |
| size_appended |
| = wlan_11h_convert_ieee_to_mrvl_ie((char *) *ppbuffer, |
| (char *) &p11h_bss_info-> |
| quiet); |
| HEXDUMP("11h: Quiet", (t_u8 *) * ppbuffer, size_appended); |
| *ppbuffer += size_appended; |
| ret_len += size_appended; |
| } |
| |
| /* Copy the local constraint from the network */ |
| local_constraint = p11h_bss_info->power_constraint.local_constraint; |
| } else { |
| /* |
| * If we are the adhoc starter, we can add a quiet element |
| */ |
| if (adapter->state_11h.quiet_ie.quiet_period) { |
| size_appended = wlan_11h_convert_ieee_to_mrvl_ie((char *) *ppbuffer, |
| (char *) &adapter-> |
| state_11h. |
| quiet_ie); |
| HEXDUMP("11h: Quiet", (t_u8 *) * ppbuffer, size_appended); |
| *ppbuffer += size_appended; |
| ret_len += size_appended; |
| |
| /* Use the local_constraint configured in the driver state */ |
| local_constraint = adapter->state_11h.usr_def_power_constraint; |
| } |
| } |
| |
| /* Set the local constraint configured in the firmware */ |
| wlan_11h_set_local_power_constraint(priv, channel, local_constraint); |
| |
| LEAVE(); |
| return ret_len; |
| } |
| |
| /** |
| * @brief Return whether the driver is currently setup to use 11h for |
| * adhoc start. |
| * |
| * Association/Join commands are dynamic in that they enable 11h in the |
| * driver/firmware when they are detected in the existing BSS. |
| * |
| * @param priv Private driver information structure |
| * |
| * @return |
| * - MTRUE if 11h is enabled |
| * - MFALSE otherwise |
| */ |
| static int |
| wlan_11h_is_enabled(mlan_private * priv) |
| { |
| wlan_11h_state_t *pstate_11h = &priv->adapter->state_11h; |
| |
| return (pstate_11h->is_11h_enabled ? MTRUE : MFALSE); |
| } |
| |
| /** |
| * @brief Query 11h firmware enabled state. |
| * |
| * Return whether the firmware currently has 11h extensions enabled |
| * |
| * @param priv Private driver information structure |
| * |
| * @return |
| * - MTRUE if 11h has been activated in the firmware |
| * - MFALSE otherwise |
| * |
| * @sa wlan_11h_activate |
| */ |
| int |
| wlan_11h_is_active(mlan_private * priv) |
| { |
| wlan_11h_state_t *pstate_11h = &priv->adapter->state_11h; |
| |
| return (pstate_11h->is_11h_active ? MTRUE : MFALSE); |
| } |
| |
| /** |
| * @brief Disable the transmit interface and record the state. |
| * |
| * @param priv Private driver information structure |
| * |
| * @return t_void |
| */ |
| t_void |
| wlan_11h_tx_disable(mlan_private * priv) |
| { |
| wlan_11h_state_t *pstate_11h = &priv->adapter->state_11h; |
| |
| ENTER(); |
| pstate_11h->tx_disabled = MTRUE; |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Enable the transmit interface and record the state. |
| * |
| * @param priv Private driver information structure |
| * |
| * @return t_void |
| */ |
| t_void |
| wlan_11h_tx_enable(mlan_private * priv) |
| { |
| wlan_11h_state_t *pstate_11h = &priv->adapter->state_11h; |
| |
| ENTER(); |
| pstate_11h->tx_disabled = MFALSE; |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Enable or Disable the 11h extensions in the firmware |
| * |
| * @param priv Private driver information structure |
| * @param flag Enable 11h if MTRUE, disable otherwise |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| int |
| wlan_11h_activate(mlan_private * priv, t_u32 flag) |
| { |
| wlan_11h_state_t *pstate_11h = &priv->adapter->state_11h; |
| int enable = flag ? 1 : 0; |
| int ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| /* |
| * Send cmd to FW to enable/disable 11h function in firmware |
| */ |
| ret = wlan_prepare_cmd(priv, |
| HostCmd_CMD_802_11_SNMP_MIB, |
| HostCmd_ACT_GEN_SET, Dot11H_i, MNULL, &enable); |
| |
| if (ret) |
| ret = MLAN_STATUS_FAILURE; |
| else |
| /* Set boolean flag in driver 11h state */ |
| pstate_11h->is_11h_active = flag; |
| |
| PRINTM(MINFO, "11h: %s\n", enable ? "Activate" : "Deactivate"); |
| |
| LEAVE(); |
| |
| return ret; |
| } |
| |
| /** |
| * |
| * @brief Initialize the 11h parameters and enable 11h when starting an IBSS |
| * |
| * @param adapter mlan_adapter structure |
| * |
| * @return t_void |
| */ |
| t_void |
| wlan_11h_init(mlan_adapter * adapter) |
| { |
| wlan_11h_state_t *pstate_11h = &adapter->state_11h; |
| IEEEtypes_Quiet_t *pquiet = &adapter->state_11h.quiet_ie; |
| |
| ENTER(); |
| |
| pstate_11h->usr_def_power_constraint = WLAN_11H_TPC_POWERCONSTRAINT; |
| pstate_11h->min_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MIN; |
| pstate_11h->max_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MAX; |
| |
| /* |
| * By default, the driver should have its preference set as 11h being |
| * activated when starting an ad hoc network. For infrastructure |
| * and ad hoc join, 11h will be sensed and activated accordingly. |
| */ |
| pstate_11h->is_11h_enabled = MTRUE; |
| |
| /* |
| * On power up, the firmware should have 11h support inactive. |
| */ |
| pstate_11h->is_11h_active = MFALSE; |
| |
| /* Initialize quiet_ie */ |
| memset(pquiet, 0, sizeof(IEEEtypes_Quiet_t)); |
| |
| pquiet->element_id = QUIET; |
| pquiet->len = (sizeof(pquiet->quiet_count) + sizeof(pquiet->quiet_period) |
| + sizeof(pquiet->quiet_duration) |
| + sizeof(pquiet->quiet_offset)); |
| LEAVE(); |
| } |
| |
| /** |
| * @brief Retrieve a randomly selected starting channel if needed for 11h |
| * |
| * If 11h is enabled and an A-Band channel start band preference |
| * configured in the driver, the start channel must be random in order |
| * to meet with |
| * |
| * @param priv Private driver information structure |
| * |
| * @return Integer starting channel |
| */ |
| int |
| wlan_11h_get_adhoc_start_channel(mlan_private * priv) |
| { |
| unsigned int start_chn; |
| mlan_adapter *adapter = priv->adapter; |
| int region; |
| int rand_entry; |
| region_chan_t *chn_tbl; |
| |
| ENTER(); |
| |
| /* |
| * Set start_chn to the Default. Used if 11h is disabled or the band |
| * does not require 11h support. |
| */ |
| start_chn = DEFAULT_AD_HOC_CHANNEL; |
| |
| /* |
| * Check that we are looking for a channel in the A Band |
| */ |
| if ((adapter->adhoc_start_band & BAND_A) |
| || (adapter->adhoc_start_band & BAND_AN) |
| ) { |
| /* |
| * Set default to the A Band default. Used if random selection fails |
| * or if 11h is not enabled |
| */ |
| start_chn = DEFAULT_AD_HOC_CHANNEL_A; |
| |
| /* |
| * Check that 11h is enabled in the driver |
| */ |
| if (wlan_11h_is_enabled(priv)) { |
| /* |
| * Search the region_channel tables for a channel table |
| * that is marked for the A Band. |
| */ |
| for (region = 0; (region < MAX_REGION_CHANNEL_NUM); region++) { |
| chn_tbl = &adapter->region_channel[region]; |
| |
| /* Check if table is valid and marked for A Band */ |
| if (chn_tbl->valid |
| && chn_tbl->region == adapter->region_code |
| && chn_tbl->band & BAND_A) { |
| /* |
| * Set the start channel. Get a random number and |
| * use it to pick an entry in the table between 0 |
| * and the number of channels in the table (NumCFP). |
| */ |
| rand_entry = wlan_11h_get_random_num() % chn_tbl->num_cfp; |
| start_chn = chn_tbl->pcfp[rand_entry].channel; |
| } |
| } |
| } |
| } |
| |
| PRINTM(MINFO, "11h: %s: AdHoc Channel set to %u\n", |
| wlan_11h_is_enabled(priv) ? "Enabled" : "Disabled", start_chn); |
| |
| LEAVE(); |
| |
| return start_chn; |
| } |
| |
| /** |
| * @brief Check if the current region's regulations require the input channel |
| * to be scanned for radar. |
| * |
| * Based on statically defined requirements for sub-bands per regulatory |
| * agency requirements. |
| * |
| * Used in adhoc start to determine if channel availability check is required |
| * |
| * @param priv Private driver information structure |
| * @param channel Channel to determine radar detection requirements |
| * |
| * @return |
| * - MTRUE if radar detection is required |
| * - MFALSE otherwise |
| * |
| * @sa wlan_11h_radar_detected |
| */ |
| int |
| wlan_11h_radar_detect_required(mlan_private * priv, t_u8 channel) |
| { |
| int required; |
| |
| ENTER(); |
| |
| /* |
| * Assume that radar detection is required unless exempted below. |
| * No checks for 11h or measurement code being enabled is placed here |
| * since regulatory requirements exist whether we support them or not. |
| */ |
| required = MTRUE; |
| |
| switch (priv->adapter->region_code) { |
| case 0x10: /* USA FCC */ |
| case 0x20: /* Canada IC */ |
| /* |
| * FCC does not yet require radar detection in the |
| * 5.25-5.35 (U-NII middle) band |
| */ |
| if (channel < wlan_11h_unii_middle_band.start_chan || |
| channel >= wlan_11h_unii_mid_upper_band.start_chan) { |
| required = MFALSE; |
| } |
| break; |
| case 0x30: /* Europe ETSI */ |
| /* |
| * Radar detection is not required in the |
| * 5.15-5.25 (U-NII lower) and 5.725-5.825 (U-NII upper) bands |
| */ |
| if (channel < wlan_11h_unii_middle_band.start_chan || |
| channel >= wlan_11h_unii_upper_band.start_chan) { |
| /* Radar detection not required */ |
| required = MFALSE; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| PRINTM(MINFO, "11h: Radar detection in region %#02x " |
| "is %srequired for channel %d\n", |
| priv->adapter->region_code, (required ? "" : "NOT "), channel); |
| |
| if (required == MTRUE && priv->media_connected == MTRUE |
| && priv->curr_bss_params.bss_descriptor.channel == channel) { |
| required = MFALSE; |
| |
| PRINTM(MINFO, "11h: Radar detection not required. " |
| "Already operating on the channel"); |
| } |
| |
| LEAVE(); |
| return required; |
| } |
| |
| /** |
| * @brief Perform a radar measurement and report the result if required on |
| * given channel |
| * |
| * Check to see if the provided channel requires a channel availability |
| * check (60 second radar detection measurement). If required, perform |
| * measurement, stalling calling thread until the measurement completes |
| * and then report result. |
| * |
| * Used when starting an adhoc network. |
| * |
| * @param priv Private driver information structure |
| * @param channel Channel on which to perform radar measurement |
| * |
| * @return |
| * - MTRUE if radar has been detected |
| * - MFALSE if radar detection is not required or radar has not been detected |
| * |
| * @sa wlan_11h_radar_detect_required |
| */ |
| int |
| wlan_11h_radar_detected(mlan_private * priv, t_u8 channel) |
| { |
| int ret; |
| HostCmd_DS_MEASUREMENT_REQUEST meas_req; |
| HostCmd_DS_MEASUREMENT_REPORT meas_rpt; |
| |
| ENTER(); |
| |
| /* |
| * If the channel requires radar, default the return value to it being |
| * detected. |
| */ |
| ret = wlan_11h_radar_detect_required(priv, channel); |
| |
| memset(&meas_req, 0x00, sizeof(meas_req)); |
| memset(&meas_rpt, 0x00, sizeof(meas_rpt)); |
| |
| /* |
| * Send a basic measurement request on the indicated channel for the |
| * required channel availability check time. |
| */ |
| meas_req.meas_type = WLAN_MEAS_BASIC; |
| meas_req.req.basic.channel = channel; |
| meas_req.req.basic.duration = WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION; |
| |
| /* |
| * Set the STA that we are requesting the measurement from to our own |
| * mac address, causing our firmware to perform the measurement itself |
| */ |
| memcpy(meas_req.mac_addr, priv->curr_addr, sizeof(meas_req.mac_addr)); |
| |
| /* |
| * Send the measurement request and timeout duration to wait for |
| * the command to spend until the measurement report is received |
| * from the firmware. If the command fails, the default ret value set |
| * above will be returned. |
| */ |
| if (!wlan_meas_util_send_req(priv, &meas_req, |
| meas_req.req.basic.duration, &meas_rpt)) { |
| /* |
| * If the report indicates no measurement was done, leave the default |
| * return value alone. |
| */ |
| if (!meas_rpt.rpt.basic.map.unmeasured) |
| /* |
| * Set the return value based on the radar indication bit |
| */ |
| ret = meas_rpt.rpt.basic.map.radar ? MTRUE : MFALSE; |
| |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Process an TLV buffer for a pending BSS Adhoc start command. |
| * |
| * Activate 11h functionality in the firmware if driver has is enabled |
| * for 11h (configured by the application via IOCTL). |
| * |
| * @param priv Private driver information structure |
| * @param ppbuffer Output parameter: Pointer to the TLV output buffer, |
| * modified on return to point after the appended 11h TLVs |
| * @param pcap_info Pointer to the capability info for the BSS to join |
| * @param channel Channel on which we are starting the IBSS |
| * @param p11h_bss_info Input/Output parameter: Pointer to the 11h BSS |
| * information for this network that we are establishing. |
| * 11h sensed flag set on output if warranted. |
| * |
| * @return Integer number of bytes appended to the TLV output |
| * buffer (ppbuffer) |
| * |
| */ |
| int |
| wlan_11h_process_start(mlan_private * priv, |
| t_u8 ** ppbuffer, |
| IEEEtypes_CapInfo_t * pcap_info, |
| t_u32 channel, wlan_11h_bss_info_t * p11h_bss_info) |
| { |
| mlan_adapter *adapter = priv->adapter; |
| int ret = 0; |
| |
| ENTER(); |
| if (wlan_11h_is_enabled(priv) |
| && ((adapter->adhoc_start_band & BAND_A) |
| || (adapter->adhoc_start_band & BAND_AN) |
| ) |
| ) { |
| if (wlan_11d_get_state(priv) == DISABLE_11D) { |
| /* No use having 11h enabled without 11d enabled */ |
| wlan_11d_enable(priv, MNULL, ENABLE_11D); |
| wlan_11d_create_dnld_countryinfo(priv, adapter->adhoc_start_band); |
| } |
| |
| /* Activate 11h functions in firmware, turns on capability bit */ |
| wlan_11h_activate(priv, MTRUE); |
| pcap_info->spectrum_mgmt = 1; |
| |
| /* Set flag indicating this BSS we are starting is using 11h */ |
| p11h_bss_info->sensed_11h = MTRUE; |
| |
| ret = wlan_11h_process_adhoc(priv, ppbuffer, channel, MNULL); |
| } else { |
| /* Deactivate 11h functions in the firmware */ |
| wlan_11h_activate(priv, MFALSE); |
| pcap_info->spectrum_mgmt = 0; |
| } |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Process an TLV buffer for a pending BSS Join command for |
| * both adhoc and infra networks |
| * |
| * The TLV command processing for a BSS join for either adhoc or |
| * infrastructure network is performed with this function. The |
| * capability bits are inspected for the IBSS flag and the appropriate |
| * local routines are called to setup the necessary TLVs. |
| * |
| * Activate 11h functionality in the firmware if the spectrum management |
| * capability bit is found in the network information for the BSS we are |
| * joining. |
| * |
| * @param priv Private driver information structure |
| * @param ppbuffer Output parameter: Pointer to the TLV output buffer, |
| * modified on return to point after the appended 11h TLVs |
| * @param pcap_info Pointer to the capability info for the BSS to join |
| * @param channel Channel on which we are joining the BSS |
| * @param p11h_bss_info Pointer to the 11h BSS information for this |
| * network that was parsed out of the scan response. |
| * |
| * @return Integer number of bytes appended to the TLV output |
| * buffer (ppbuffer), MLAN_STATUS_FAILURE (-1), |
| * or MLAN_STATUS_SUCCESS (0) |
| */ |
| int |
| wlan_11h_process_join(mlan_private * priv, |
| t_u8 ** ppbuffer, |
| IEEEtypes_CapInfo_t * pcap_info, |
| t_u32 channel, wlan_11h_bss_info_t * p11h_bss_info) |
| { |
| int ret = 0; |
| |
| ENTER(); |
| |
| if (priv->media_connected == MTRUE) { |
| if (wlan_11h_is_active(priv) == p11h_bss_info->sensed_11h) { |
| /* Assume DFS parameters are the same for roaming as long as the |
| current & next APs have the same spectrum mgmt capability bit |
| setting */ |
| ret = MLAN_STATUS_SUCCESS; |
| |
| } else { |
| /* No support for roaming between DFS/non-DFS yet */ |
| ret = MLAN_STATUS_FAILURE; |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| if (p11h_bss_info->sensed_11h) { |
| /* No use having 11h enabled without 11d enabled */ |
| wlan_11d_enable(priv, MNULL, ENABLE_11D); |
| wlan_11d_parse_dnld_countryinfo(priv, priv->pattempted_bss_desc); |
| |
| /* Activate 11h functions in firmware, turns on capability bit */ |
| wlan_11h_activate(priv, MTRUE); |
| pcap_info->spectrum_mgmt = 1; |
| |
| if (pcap_info->ibss) { |
| PRINTM(MINFO, "11h: Adhoc join: Sensed\n"); |
| ret = wlan_11h_process_adhoc(priv, ppbuffer, channel, |
| p11h_bss_info); |
| } else { |
| PRINTM(MINFO, "11h: Infra join: Sensed\n"); |
| ret = wlan_11h_process_infra_join(priv, ppbuffer, |
| channel, p11h_bss_info); |
| } |
| } else { |
| /* Deactivate 11h functions in the firmware */ |
| wlan_11h_activate(priv, MFALSE); |
| pcap_info->spectrum_mgmt = 0; |
| } |
| |
| LEAVE(); |
| |
| return ret; |
| } |
| |
| /** |
| * |
| * @brief Prepare the HostCmd_DS_Command structure for an 11h 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_802_11_TPC_ADAPT_REQ |
| * - HostCmd_CMD_802_11_TPC_INFO |
| * - HostCmd_CMD_802_11_CHAN_SW_ANN |
| * |
| * @param priv Private driver information structure |
| * @param pcmd_ptr Output parameter: Pointer to the command being prepared |
| * for the firmware |
| * @param pinfo_buf Void buffer pass through with data necessary for a |
| * specific command type |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| * |
| * @sa wlan_11h_cmd_tpc_request |
| * @sa wlan_11h_cmd_tpc_info |
| * @sa wlan_11h_cmd_chan_sw_ann |
| */ |
| int |
| wlan_11h_cmd_process(mlan_private * priv, |
| HostCmd_DS_COMMAND * pcmd_ptr, const t_void * pinfo_buf) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| switch (pcmd_ptr->command) { |
| case HostCmd_CMD_802_11_TPC_ADAPT_REQ: |
| ret = wlan_11h_cmd_tpc_request(priv, pcmd_ptr, pinfo_buf); |
| break; |
| case HostCmd_CMD_802_11_TPC_INFO: |
| ret = wlan_11h_cmd_tpc_info(priv, pcmd_ptr, pinfo_buf); |
| break; |
| case HostCmd_CMD_802_11_CHAN_SW_ANN: |
| ret = wlan_11h_cmd_chan_sw_ann(priv, pcmd_ptr, pinfo_buf); |
| 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 if from an 11h command |
| * |
| * Use the Command field to determine if the command response being |
| * is for 11h. Call the local command response handler accordingly for: |
| * |
| * - HostCmd_CMD_802_11_TPC_ADAPT_REQ |
| * - HostCmd_CMD_802_11_TPC_INFO |
| * - HostCmd_CMD_802_11_CHAN_SW_ANN |
| * |
| * @param priv 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_11h_cmdresp_process(mlan_private * priv, const HostCmd_DS_COMMAND * resp) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| switch (resp->command) { |
| case HostCmd_CMD_802_11_TPC_ADAPT_REQ: |
| HEXDUMP("11h: TPC REQUEST Rsp:", (t_u8 *) resp, (int) resp->size); |
| memcpy(priv->adapter->curr_cmd->pdata_buf, |
| &resp->params.tpc_req, sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ)); |
| break; |
| |
| case HostCmd_CMD_802_11_TPC_INFO: |
| HEXDUMP("11h: TPC INFO Rsp Data:", (t_u8 *) resp, (int) resp->size); |
| break; |
| |
| case HostCmd_CMD_802_11_CHAN_SW_ANN: |
| PRINTM(MINFO, "11h: Ret ChSwAnn: Sz=%u, Seq=%u, Ret=%u\n", |
| resp->size, resp->seq_num, resp->result); |
| break; |
| |
| default: |
| ret = MLAN_STATUS_FAILURE; |
| } |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Process an element from a scan response, copy relevant info for 11h |
| * |
| * @param p11h_bss_info Output parameter: Pointer to the 11h BSS information |
| * for the network that is being processed |
| * @param pelement Pointer to the current IE we are inspecting for 11h |
| * relevance |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| int |
| wlan_11h_process_bss_elem(wlan_11h_bss_info_t * p11h_bss_info, |
| const t_u8 * pelement) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| switch (*pelement) { |
| case POWER_CONSTRAINT: |
| PRINTM(MINFO, "11h: Power Constraint IE Found\n"); |
| p11h_bss_info->sensed_11h = 1; |
| memcpy(&p11h_bss_info->power_constraint, pelement, |
| sizeof(IEEEtypes_PowerConstraint_t)); |
| break; |
| |
| case POWER_CAPABILITY: |
| PRINTM(MINFO, "11h: Power Capability IE Found\n"); |
| p11h_bss_info->sensed_11h = 1; |
| memcpy(&p11h_bss_info->power_capability, pelement, |
| sizeof(IEEEtypes_PowerCapability_t)); |
| break; |
| |
| case TPC_REPORT: |
| PRINTM(MINFO, "11h: Tpc Report IE Found\n"); |
| p11h_bss_info->sensed_11h = 1; |
| memcpy(&p11h_bss_info->tpc_report, pelement, |
| sizeof(IEEEtypes_TPCReport_t)); |
| break; |
| |
| case CHANNEL_SWITCH_ANN: |
| p11h_bss_info->sensed_11h = 1; |
| PRINTM(MINFO, "11h: Channel Switch Ann IE Found\n"); |
| break; |
| |
| case QUIET: |
| PRINTM(MINFO, "11h: Quiet IE Found\n"); |
| p11h_bss_info->sensed_11h = 1; |
| memcpy(&p11h_bss_info->quiet, pelement, sizeof(IEEEtypes_Quiet_t)); |
| break; |
| |
| case IBSS_DFS: |
| PRINTM(MINFO, "11h: Ibss Dfs IE Found\n"); |
| p11h_bss_info->sensed_11h = 1; |
| memcpy(&p11h_bss_info->ibss_dfs, pelement, |
| sizeof(IEEEtypes_IBSS_DFS_t)); |
| break; |
| |
| case SUPPORTED_CHANNELS: |
| case TPC_REQUEST: |
| /* |
| * These elements are not in beacons/probe responses. Included here |
| * to cover set of enumerated 11h elements. |
| */ |
| break; |
| |
| default: |
| ret = MLAN_STATUS_FAILURE; |
| } |
| LEAVE(); |
| return ret; |
| } |