/** @file mlan2040misc.c * * @brief This file contains helper functions for coex application * * Copyright (C) 2009-2019, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. * */ /************************************************************************ Change log: 06/24/2009: initial version ************************************************************************/ #include #include #include #include #include "mlan2040coex.h" #include "mlan2040misc.h" /******************************************************** Local Variables ********************************************************/ /** Regulatory class and Channel mapping for various regions */ static class_chan_t us_class_chan_t[] = { {32, {1, 2, 3, 4, 5, 6, 7}, 7}, {33, {5, 6, 7, 8, 9, 10, 11}, 7} }; static class_chan_t europe_class_chan_t[] = { {11, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 9}, {12, {5, 6, 7, 8, 9, 10, 11, 12, 13}, 9} }; static class_chan_t japan_class_chan_t[] = { {56, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 9}, {57, {5, 6, 7, 8, 9, 10, 11, 12, 13}, 9}, {58, {14}, 1} }; /** Region-code(Regulatory domain) and Class-channel table mapping */ static region_class_chan_t region_class_chan_table[] = { {0x10, us_class_chan_t, sizeof(us_class_chan_t) / sizeof(class_chan_t)} /* US */ , {0x20, us_class_chan_t, sizeof(us_class_chan_t) / sizeof(class_chan_t)} /* CANADA */ , {0x30, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)} /* EUROPE */ , {0x32, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)} /* FRANCE */ , {0x40, japan_class_chan_t, sizeof(japan_class_chan_t) / sizeof(class_chan_t)} /* JAPAN */ , {0x41, japan_class_chan_t, sizeof(japan_class_chan_t) / sizeof(class_chan_t)} /* JAPAN */ , {0x50, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)} /* CHINA */ }; /******************************************************** Global Variables ********************************************************/ /******************************************************** Local Functions ********************************************************/ /** * @brief This function prepares the channel list for a particular * regulatory class from channel number for legacy AP * @param cur_class_chan_table A pointer to the class_chan_t * @param num_entry Number of entry in cur_class_chan_table table * @param chan_list A pointer to the output channel list * @param chan_num total number of channel in output channel list * @param reg_domain regulatory domain * @param reg_class regulatory class * @param is_intol_ap_present It sets TRUE when 40MHz intolerant AP is found * otherwise FALSE * @return None */ static void get_channels_for_specified_reg_class(class_chan_t *cur_class_chan_table, int num_entry, t_u8 *chan_list, t_u8 *chan_num, t_u8 reg_domain, t_u8 reg_class, t_u8 *is_intol_ap_present) { int i, j, k, idx = 0; *is_intol_ap_present = FALSE; /* For each regulatory class */ for (i = 0; i < num_entry; i++) { if (cur_class_chan_table[i].reg_class == reg_class) { /* For each channel of the regulatory class */ for (j = 0; j < cur_class_chan_table[i].total_chan; j++) { for (k = 0; k < num_leg_ap_chan; k++) { if (cur_class_chan_table[i]. channels[j] == leg_ap_chan_list[k].chan_num) { *(chan_list + idx) = leg_ap_chan_list[k]. chan_num; idx++; if (leg_ap_chan_list[k]. is_intol_set) *is_intol_ap_present = TRUE; } } } break; } } /* update the total number of channel */ *chan_num = idx--; return; } /******************************************************** Global Functions ********************************************************/ /** * @brief Prepare 2040 coex command buffer * @param buf A pointer to the command buffer * @param chan_list Channel list * @param num_of_chan Number of channel present in channel list * @param reg_class Regulatory class * @param is_intol_ap_present Flag: is any 40 MHz intolerant AP * is present in these chaanel set * @return MLAN_STATUS_SUCCESS--success, otherwise--fail */ void prepare_coex_cmd_buff(t_u8 *buf, t_u8 *chan_list, t_u8 num_of_chan, t_u8 reg_class, t_u8 is_intol_ap_present) { HostCmd_DS_GEN *hostcmd; MrvlIETypes_2040COEX_t *coex_ie = NULL; MrvlIETypes_2040BssIntolerantChannelReport_t *bss_intol_ie = NULL; t_u8 *pos = NULL; int intol; hostcmd = (HostCmd_DS_GEN *)(buf + sizeof(t_u32)); hostcmd->command = cpu_to_le16(HostCmd_CMD_11N_2040COEX); hostcmd->size = S_DS_GEN; pos = buf + sizeof(t_u32) + S_DS_GEN; { coex_ie = (MrvlIETypes_2040COEX_t *)pos; coex_ie->header.element_id = TLV_ID_2040COEX; coex_ie->header.len = sizeof(coex_ie->coex_elem); /* Check STA is 40 MHz intolerant or not */ is_intolerant_sta(&intol); if (intol) coex_ie->coex_elem |= MBIT(1); if (is_intol_ap_present) coex_ie->coex_elem |= MBIT(2); pos += sizeof(MrvlIETypes_2040COEX_t); hostcmd->size += sizeof(MrvlIETypes_2040COEX_t); } { bss_intol_ie = (MrvlIETypes_2040BssIntolerantChannelReport_t *)pos; bss_intol_ie->header.element_id = TLV_ID_2040BSS_INTOL_CHAN_REPORT; hostcmd->size += sizeof(MrvlIETypes_2040BssIntolerantChannelReport_t) - sizeof(bss_intol_ie->chan_num); bss_intol_ie->reg_class = reg_class; memcpy(bss_intol_ie->chan_num, chan_list, num_of_chan); bss_intol_ie->header.len = sizeof(bss_intol_ie->reg_class) + num_of_chan; hostcmd->size += num_of_chan; } hostcmd->size = cpu_to_le16(hostcmd->size); return; } /** * @brief Invoke multiple 2040Coex commands for multiple regulatory classes * * @return MLAN_STATUS_SUCCESS--success, otherwise--fail */ int invoke_coex_command(void) { int cur_reg_domain; t_u8 chan_list[MAX_CHAN], is_intol_ap_present; t_u8 num_of_chan; int i, num_entry, ret = MLAN_STATUS_SUCCESS; class_chan_t *cur_class_chan_table = NULL; /** get region code */ ret = get_region_code(&cur_reg_domain); if (ret != MLAN_STATUS_SUCCESS) return ret; /** Find region_class_chan_table for this region */ for (i = 0; (unsigned int)i < (sizeof(region_class_chan_table) / sizeof(region_class_chan_t)); i++) { if (region_class_chan_table[i].reg_domain == cur_reg_domain) { cur_class_chan_table = region_class_chan_table[i].class_chan_list; num_entry = region_class_chan_table[i].num_class_chan_entry; break; } } if (cur_class_chan_table == NULL) { printf("No region_class_chan table found for this region\n"); return MLAN_STATUS_FAILURE; } for (i = 0; i < num_entry; i++) { /** Get channels for the specified regulatory class */ get_channels_for_specified_reg_class(cur_class_chan_table, num_entry, chan_list, &num_of_chan, cur_reg_domain, cur_class_chan_table[i]. reg_class, &is_intol_ap_present); /** If any channel found for this regulatory class, then invoke the 2040coex command */ if (num_of_chan > 0) { ret = process_host_cmd(CMD_2040COEX, chan_list, num_of_chan, cur_class_chan_table[i]. reg_class, is_intol_ap_present); if (ret) break; } } return ret; } /** * @brief Process host_cmd response * * @param cmd_name The command string * @param buf A pointer to the response buffer * * @return MLAN_STATUS_SUCCESS--success, otherwise--fail */ int process_host_cmd_resp(char *cmd_name, t_u8 *buf) { t_u32 hostcmd_size = 0; HostCmd_DS_GEN *hostcmd = NULL; int ret = MLAN_STATUS_SUCCESS; buf += strlen(CMD_MARVELL) + strlen(cmd_name); memcpy((t_u8 *)&hostcmd_size, buf, sizeof(t_u32)); buf += sizeof(t_u32); hostcmd = (HostCmd_DS_GEN *)buf; hostcmd->command = le16_to_cpu(hostcmd->command); hostcmd->size = le16_to_cpu(hostcmd->size); hostcmd->command &= ~HostCmd_RET_BIT; if (!le16_to_cpu(hostcmd->result)) { switch (hostcmd->command) { } } else { printf("HOSTCMD failed: ReturnCode=%#04x, Result=%#04x\n", le16_to_cpu(hostcmd->command), le16_to_cpu(hostcmd->result)); ret = MLAN_STATUS_FAILURE; } return ret; }