| /** @file mlan2040coex.c |
| * |
| * @brief 11n 20/40 MHz Coex application |
| * |
| * Copyright (C) 2009-2017, 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 <stdio.h> |
| #include <ctype.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <sys/ioctl.h> |
| #include <linux/if.h> |
| #include <linux/wireless.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #ifdef ANDROID |
| #include <net/if_ether.h> |
| #else |
| #include <net/ethernet.h> |
| #endif |
| #include "mlan2040coex.h" |
| #include "mlan2040misc.h" |
| |
| /** coex application's version number */ |
| #define COEX_VER "M2.0" |
| |
| /** Initial number of total private ioctl calls */ |
| #define IW_INIT_PRIV_NUM 128 |
| /** Maximum number of total private ioctl calls supported */ |
| #define IW_MAX_PRIV_NUM 1024 |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| static char *usage[] = { |
| "Usage: ", |
| " mlan2040coex [-i <intfname>] [-hvB] ", |
| " -h = help", |
| " -v = version", |
| " -B = run the process in background.", |
| " (if intfname not present then mlan0 assumed)" |
| }; |
| |
| t_s32 sockfd = 0; /**< socket descriptor */ |
| char dev_name[IFNAMSIZ + 1]; /**< device name */ |
| |
| /** Flag: is 2040coex command required */ |
| int coex_cmd_req_flag = FALSE; |
| /** Flag: is associated */ |
| int assoc_flag = FALSE; |
| /** Flag: is HT AP */ |
| int is_ht_ap = FALSE; |
| /** terminate flag */ |
| int terminate_flag = FALSE; |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| /** OBSS scan parameter of associated AP */ |
| OBSSScanParam_t cur_obss_scan_param; |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| |
| /** |
| * @brief Prepare command buffer |
| * @param buffer Command buffer to be filled |
| * @param cmd Command id |
| * @param num Number of arguments |
| * @param args Arguments list |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| static int |
| prepare_buffer(t_u8 *buffer, char *cmd, t_u32 num, char *args[]) |
| { |
| t_u8 *pos = NULL; |
| unsigned int i = 0; |
| |
| memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER); |
| |
| /* Flag it for our use */ |
| pos = buffer; |
| strncpy((char *)pos, CMD_MARVELL, strlen(CMD_MARVELL)); |
| pos += (strlen(CMD_MARVELL)); |
| |
| /* Insert command */ |
| strncpy((char *)pos, (char *)cmd, strlen(cmd)); |
| pos += (strlen(cmd)); |
| |
| /* Insert arguments */ |
| for (i = 0; i < num; i++) { |
| strncpy((char *)pos, args[i], strlen(args[i])); |
| pos += strlen(args[i]); |
| if (i < (num - 1)) { |
| strncpy((char *)pos, " ", strlen(" ")); |
| pos += 1; |
| } |
| } |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Process OBSS scan table |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| int |
| process_scantable(void) |
| { |
| char ssid[MRVDRV_MAX_SSID_LENGTH + 1] = { 0 }; |
| unsigned int scan_start; |
| int idx, i = 0, j, already_listed, ssid_len = 0, ssid_idx; |
| |
| t_u8 *pcurrent; |
| t_u8 *pnext; |
| IEEEtypes_ElementId_e *pelement_id; |
| t_u8 *pelement_len, ht_cap_present, intol_bit_is_set; |
| int ret = MLAN_STATUS_SUCCESS; |
| t_s32 bss_info_len = 0; |
| t_u32 fixed_field_length = 0; |
| |
| IEEEtypes_CapInfo_t cap_info; |
| t_u8 tsf[8]; |
| t_u16 beacon_interval; |
| IEEEtypes_HTCap_t *pht_cap; |
| |
| wlan_ioctl_get_scan_table_info *prsp_info; |
| wlan_get_scan_table_fixed fixed_fields; |
| |
| t_u8 *buffer = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| |
| /* Start preparing the buffer */ |
| /* Initialize buffer */ |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| memset(&cap_info, 0x00, sizeof(cap_info)); |
| memset(leg_ap_chan_list, 0, sizeof(leg_ap_chan_list)); |
| num_leg_ap_chan = 0; |
| |
| scan_start = 1; |
| |
| do { |
| /* buffer = CMD_MARVELL + <cmd_string> */ |
| prepare_buffer(buffer, "getscantable", 0, NULL); |
| prsp_info = |
| (wlan_ioctl_get_scan_table_info *)(buffer + |
| strlen(CMD_MARVELL) + |
| strlen |
| ("getscantable")); |
| |
| prsp_info->scan_number = scan_start; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| if (errno == EAGAIN) { |
| ret = -EAGAIN; |
| } else { |
| perror("mlan2040coex"); |
| fprintf(stderr, |
| "mlan2040coex: getscantable fail\n"); |
| ret = MLAN_STATUS_FAILURE; |
| } |
| goto done; |
| } |
| |
| prsp_info = (wlan_ioctl_get_scan_table_info *)buffer; |
| pcurrent = 0; |
| pnext = prsp_info->scan_table_entry_buf; |
| |
| if (scan_start == 1) { |
| printf("----------------------------------------------\n"); |
| } |
| |
| for (idx = 0; (unsigned int)idx < prsp_info->scan_number; idx++) { |
| |
| /* |
| * Set pcurrent to pnext in case pad bytes are at the end |
| * of the last IE we processed. |
| */ |
| pcurrent = pnext; |
| |
| memcpy((t_u8 *)&fixed_field_length, |
| (t_u8 *)pcurrent, sizeof(fixed_field_length)); |
| pcurrent += sizeof(fixed_field_length); |
| |
| memcpy((t_u8 *)&bss_info_len, |
| (t_u8 *)pcurrent, sizeof(bss_info_len)); |
| pcurrent += sizeof(bss_info_len); |
| |
| memcpy((t_u8 *)&fixed_fields, |
| (t_u8 *)pcurrent, sizeof(fixed_fields)); |
| pcurrent += fixed_field_length; |
| |
| pnext = pcurrent + bss_info_len; |
| |
| if (bss_info_len >= (sizeof(tsf) |
| + sizeof(beacon_interval) + |
| sizeof(cap_info))) { |
| pcurrent += |
| (sizeof(tsf) + sizeof(beacon_interval) + |
| sizeof(cap_info)); |
| bss_info_len -= |
| (sizeof(tsf) + sizeof(beacon_interval) + |
| sizeof(cap_info)); |
| } |
| ht_cap_present = FALSE; |
| intol_bit_is_set = FALSE; |
| memset(ssid, 0, MRVDRV_MAX_SSID_LENGTH + 1); |
| ssid_len = 0; |
| while (bss_info_len >= 2) { |
| pelement_id = (IEEEtypes_ElementId_e *)pcurrent; |
| pelement_len = pcurrent + 1; |
| pcurrent += 2; |
| |
| switch (*pelement_id) { |
| case SSID: |
| if (*pelement_len && |
| *pelement_len <= |
| MRVDRV_MAX_SSID_LENGTH) { |
| memcpy(ssid, pcurrent, |
| *pelement_len); |
| ssid_len = *pelement_len; |
| } |
| break; |
| |
| case HT_CAPABILITY: |
| pht_cap = |
| (IEEEtypes_HTCap_t *) |
| pelement_id; |
| ht_cap_present = TRUE; |
| if (IS_INTOL_BIT_SET |
| (le16_to_cpu |
| (pht_cap->ht_cap.ht_cap_info))) { |
| intol_bit_is_set = TRUE; |
| } |
| break; |
| default: |
| break; |
| } |
| pcurrent += *pelement_len; |
| bss_info_len -= (2 + *pelement_len); |
| } |
| if (!ht_cap_present || intol_bit_is_set) { |
| printf("%s AP found on channel number: %-3d ", |
| intol_bit_is_set ? "40 MHZ intolerant" : |
| "Legacy", fixed_fields.channel); |
| if (ssid_len) { |
| printf("SSID: "); |
| /* Print out the ssid or the hex values if non-printable */ |
| for (ssid_idx = 0; ssid_idx < ssid_len; |
| ssid_idx++) { |
| if (isprint(ssid[ssid_idx])) { |
| printf("%c", |
| ssid[ssid_idx]); |
| } else { |
| printf("\\%02x", |
| ssid[ssid_idx]); |
| } |
| } |
| } |
| printf("\n"); |
| |
| /* Verify that the channel is already listed or not */ |
| already_listed = FALSE; |
| for (j = 0; j < i; j++) { |
| if (leg_ap_chan_list[j].chan_num == |
| fixed_fields.channel) { |
| already_listed = TRUE; |
| if (intol_bit_is_set) |
| leg_ap_chan_list[j]. |
| is_intol_set = |
| intol_bit_is_set; |
| break; |
| } |
| } |
| if (!already_listed) { |
| /* add the channel in list */ |
| leg_ap_chan_list[i].chan_num = |
| fixed_fields.channel; |
| leg_ap_chan_list[i].is_intol_set = |
| intol_bit_is_set; |
| i++; |
| coex_cmd_req_flag = TRUE; |
| num_leg_ap_chan++; |
| } |
| } |
| } |
| scan_start += prsp_info->scan_number; |
| |
| } while (prsp_info->scan_number); |
| |
| done: |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| return ret; |
| } |
| |
| /** BSS Mode any (both infra and adhoc) */ |
| #define BSS_MODE_ANY 3 |
| |
| /** current scan config parameters */ |
| #define SCAN_CFG_PARAMS 7 |
| |
| /** Active : 1 , Passive : 2 */ |
| #define SCAN_TYPE_ACTIVE 1 |
| |
| /** |
| * @brief Issue get scan type command |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| static int |
| get_scan_cfg(int *scan_param) |
| { |
| t_u8 *buffer = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| int ret = MLAN_STATUS_SUCCESS; |
| |
| /* Start preparing the buffer */ |
| /* Initialize buffer */ |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /* buffer = CMD_MARVELL + <cmd_string> */ |
| prepare_buffer(buffer, "scancfg", 0, NULL); |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| perror("mlan2040coex"); |
| fprintf(stderr, "mlan2040coex: get_scan_cfg fail\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| scan_param = (int *)(buffer); |
| done: |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Issue OBSS scan command |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| int |
| process_setuserscan(void) |
| { |
| wlan_ioctl_user_scan_cfg scan_req; |
| int scan_cfg[SCAN_CFG_PARAMS]; |
| t_u8 *buffer = NULL, *pos = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| int status = 0; |
| |
| memset(&scan_req, 0x00, sizeof(scan_req)); |
| memset(scan_cfg, 0x00, SCAN_CFG_PARAMS); |
| coex_cmd_req_flag = FALSE; |
| |
| if (get_scan_cfg(scan_cfg) != MLAN_STATUS_SUCCESS) { |
| printf("mlan2040coex: scancfg ioctl failure"); |
| return -EFAULT; |
| } |
| |
| if (scan_cfg[0] == SCAN_TYPE_ACTIVE) |
| scan_req.chan_list[0].scan_time = |
| (t_u32)le16_to_cpu(cur_obss_scan_param. |
| obss_scan_active_dwell); |
| else |
| scan_req.chan_list[0].scan_time = |
| (t_u32)le16_to_cpu(cur_obss_scan_param. |
| obss_scan_passive_total); |
| scan_req.bss_mode = (scan_cfg[1]) ? scan_cfg[1] : BSS_MODE_ANY; |
| |
| /* Start preparing the buffer */ |
| /* Initialize buffer */ |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /* buffer = CMD_MARVELL + <cmd_string> */ |
| prepare_buffer(buffer, "setuserscan", 0, NULL); |
| pos = buffer + strlen(CMD_MARVELL) + strlen("setuserscan"); |
| |
| /* buffer = buffer + 'scan_req' */ |
| memcpy(pos, &scan_req, sizeof(wlan_ioctl_user_scan_cfg)); |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| free(buffer); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| perror("mlan2040coex"); |
| fprintf(stderr, "mlan2040coex: setuserscan fail\n"); |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /** process scan results */ |
| do { |
| status = process_scantable(); |
| } while (status == -EAGAIN); |
| |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Display usage |
| * |
| * @return NA |
| */ |
| static t_void |
| display_usage(t_void) |
| { |
| t_u32 i; |
| for (i = 0; i < NELEMENTS(usage); i++) |
| fprintf(stderr, "%s\n", usage[i]); |
| } |
| |
| /** |
| * @brief get connection status |
| * |
| * @param data Pointer to the output buffer holding connection status |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| int |
| get_connstatus(int *data) |
| { |
| struct ether_addr apaddr; |
| struct ether_addr etherzero = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; |
| t_u8 *buffer = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| |
| /* Initialize buffer */ |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /* buffer = CMD_MARVELL + <cmd_string> */ |
| prepare_buffer(buffer, "getwap", 0, NULL); |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| free(buffer); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| perror("mlan2040coex"); |
| fprintf(stderr, "mlan2040coex: getwap fail\n"); |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| memset(&apaddr, 0, sizeof(struct ether_addr)); |
| memcpy(&apaddr, (struct ether_addr *)(buffer), |
| sizeof(struct ether_addr)); |
| |
| if (!memcmp(&apaddr, ðerzero, sizeof(struct ether_addr))) { |
| /* not associated */ |
| *data = FALSE; |
| } else { |
| /* associated */ |
| *data = TRUE; |
| } |
| |
| if (buffer) |
| free(buffer); |
| if (cmd) |
| free(cmd); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief Print connect and disconnect event related information |
| * |
| * @param buffer Pointer to received event buffer |
| * @param size Length of the received event |
| * |
| * @return N/A |
| */ |
| void |
| print_event_drv_connected(t_u8 *buffer, t_u16 size) |
| { |
| struct ether_addr *wap; |
| struct ether_addr etherzero = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; |
| char buf[32]; |
| |
| wap = (struct ether_addr *)(buffer + strlen(CUS_EVT_AP_CONNECTED)); |
| |
| if (!memcmp(wap, ðerzero, sizeof(struct ether_addr))) { |
| printf("---< Disconnected from AP >---\n"); |
| memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param)); |
| assoc_flag = FALSE; |
| is_ht_ap = FALSE; |
| } else { |
| memset(buf, 0, sizeof(buf)); |
| snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", |
| wap->ether_addr_octet[0], |
| wap->ether_addr_octet[1], |
| wap->ether_addr_octet[2], |
| wap->ether_addr_octet[3], |
| wap->ether_addr_octet[4], wap->ether_addr_octet[5]); |
| printf("---< Connected to AP: %s >---\n", buf); |
| /** set TRUE, if connected */ |
| assoc_flag = TRUE; |
| } |
| } |
| |
| /** |
| * @brief Parse and print received event information |
| * |
| * @param event Pointer to received event |
| * @param size Length of the received event |
| * @param evt_conn A pointer to a output buffer. It sets TRUE when it gets |
| * the event CUS_EVT_OBSS_SCAN_PARAM, otherwise FALSE |
| * @param if_name The interface name |
| * |
| * @return N/A |
| */ |
| void |
| print_event(event_header *event, t_u16 size, int *evt_conn, char *if_name) |
| { |
| if (!strncmp |
| (CUS_EVT_AP_CONNECTED, (char *)event, |
| strlen(CUS_EVT_AP_CONNECTED))) { |
| if (strlen(if_name)) |
| printf("EVENT for interface %s\n", if_name); |
| print_event_drv_connected((t_u8 *)event, size); |
| return; |
| } |
| if (!strncmp |
| (CUS_EVT_OBSS_SCAN_PARAM, (char *)event, |
| strlen(CUS_EVT_OBSS_SCAN_PARAM))) { |
| if (strlen(if_name)) |
| printf("EVENT for interface %s\n", if_name); |
| printf("---< %s >---\n", CUS_EVT_OBSS_SCAN_PARAM); |
| memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param)); |
| memcpy(&cur_obss_scan_param, |
| ((t_u8 *)event + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1), |
| sizeof(cur_obss_scan_param)); |
| /** set TRUE, since it is an HT AP */ |
| is_ht_ap = TRUE; |
| *evt_conn = TRUE; |
| return; |
| } |
| if (!strncmp |
| (CUS_EVT_BW_CHANGED, (char *)event, strlen(CUS_EVT_BW_CHANGED))) { |
| if (strlen(if_name)) |
| printf("EVENT for interface %s\n", if_name); |
| printf("---< %s >---\n", CUS_EVT_BW_CHANGED); |
| return; |
| } |
| } |
| |
| /** |
| * @brief This function parses for NETLINK events |
| * |
| * @param nlh Pointer to Netlink message header |
| * @param bytes_read Number of bytes to be read |
| * @param evt_conn A pointer to a output buffer. It sets TRUE when it gets |
| * the event CUS_EVT_OBSS_SCAN_PARAM, otherwise FALSE |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static int |
| drv_nlevt_handler(struct nlmsghdr *nlh, int bytes_read, int *evt_conn) |
| { |
| int len, plen; |
| t_u8 *buffer = NULL; |
| t_u32 event_id = 0; |
| event_header *event = NULL; |
| char if_name[IFNAMSIZ + 1]; |
| |
| /* Initialize receive buffer */ |
| buffer = (t_u8 *)malloc(NL_MAX_PAYLOAD); |
| if (!buffer) { |
| printf("ERR: Could not alloc buffer\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| memset(buffer, 0, NL_MAX_PAYLOAD); |
| |
| *evt_conn = FALSE; |
| while ((unsigned int)bytes_read >= NLMSG_HDRLEN) { |
| len = nlh->nlmsg_len; /* Length of message including header */ |
| plen = len - NLMSG_HDRLEN; |
| if (len > bytes_read || plen < 0) { |
| /* malformed netlink message */ |
| return MLAN_STATUS_FAILURE; |
| } |
| if ((unsigned int)len > NLMSG_SPACE(NL_MAX_PAYLOAD)) { |
| printf("ERR:Buffer overflow!\n"); |
| return MLAN_STATUS_FAILURE; |
| } |
| memset(buffer, 0, NL_MAX_PAYLOAD); |
| memcpy(buffer, NLMSG_DATA(nlh), plen); |
| |
| if (NLMSG_OK(nlh, len)) { |
| memcpy(&event_id, buffer, sizeof(event_id)); |
| |
| if (((event_id & 0xFF000000) == 0x80000000) || |
| ((event_id & 0xFF000000) == 0)) { |
| event = (event_header *)buffer; |
| } else { |
| memset(if_name, 0, IFNAMSIZ + 1); |
| memcpy(if_name, buffer, IFNAMSIZ); |
| event = (event_header *)(buffer + IFNAMSIZ); |
| } |
| } |
| |
| print_event(event, bytes_read, evt_conn, if_name); |
| len = NLMSG_ALIGN(len); |
| bytes_read -= len; |
| nlh = (struct nlmsghdr *)((char *)nlh + len); |
| } |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** Maximum event message length */ |
| #define MAX_MSG_LENGTH 1024 |
| |
| /** |
| * @brief Configure and read event data from netlink socket |
| * |
| * @param nl_sk Netlink socket handler |
| * @param msg Pointer to message header |
| * @param ptv Pointer to struct timeval |
| * |
| * @return Number of bytes read or MLAN_STATUS_FAILURE |
| */ |
| int |
| read_event(int nl_sk, struct msghdr *msg, struct timeval *ptv) |
| { |
| int count = -1; |
| int ret = MLAN_STATUS_FAILURE; |
| fd_set rfds; |
| |
| /* Setup read fds and initialize event buffers */ |
| FD_ZERO(&rfds); |
| FD_SET(nl_sk, &rfds); |
| |
| /* Wait for reply */ |
| ret = select(nl_sk + 1, &rfds, NULL, NULL, ptv); |
| |
| if (ret == MLAN_STATUS_FAILURE) { |
| /* Error */ |
| terminate_flag++; |
| ptv->tv_sec = DEFAULT_SCAN_INTERVAL; |
| ptv->tv_usec = 0; |
| goto done; |
| } else if (!ret) { |
| if (assoc_flag && is_ht_ap) { |
| /** Issue OBSS scan */ |
| process_setuserscan(); |
| /** Invoke 2040coex command, if any legacy AP found or |
| * any AP has 40MHz intolarent bit set */ |
| if (coex_cmd_req_flag) |
| invoke_coex_command(); |
| } |
| if (assoc_flag && is_ht_ap) { |
| /* Timeout. Try again after BSS channel width triger scan |
| interval when the STA is connected with a HT AP */ |
| ptv->tv_sec = |
| (t_u32)le16_to_cpu(cur_obss_scan_param. |
| bss_chan_width_trigger_scan_int); |
| } else { |
| /* Timeout. Try again after default duration when the STA is |
| not connected with a HT AP */ |
| ptv->tv_sec = DEFAULT_SCAN_INTERVAL; |
| } |
| ptv->tv_usec = 0; |
| goto done; |
| } |
| |
| if (!FD_ISSET(nl_sk, &rfds)) { |
| /* Unexpected error. Try again */ |
| ptv->tv_sec = DEFAULT_SCAN_INTERVAL; |
| ptv->tv_usec = 0; |
| goto done; |
| } |
| /* Success */ |
| count = recvmsg(nl_sk, msg, 0); |
| |
| done: |
| return count; |
| } |
| |
| /** Maximum event message length */ |
| #define MAX_MSG_LENGTH 1024 |
| /** |
| * @brief Run the application |
| * |
| * @param nl_sk Netlink socket |
| * |
| * @return N/A |
| */ |
| void |
| run_app(int nl_sk) |
| { |
| struct timeval tv; |
| int bytes_read, evt_conn; |
| struct msghdr msg; |
| struct sockaddr_nl dest_addr; |
| struct nlmsghdr *nlh; |
| struct iovec iov; |
| |
| /** Get connection status */ |
| if (get_connstatus(&assoc_flag) == MLAN_STATUS_FAILURE) |
| return; |
| |
| /* Initialize timeout value */ |
| tv.tv_sec = DEFAULT_SCAN_INTERVAL; |
| tv.tv_usec = 0; |
| |
| /* Initialize netlink header */ |
| nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(NL_MAX_PAYLOAD)); |
| if (!nlh) { |
| printf("ERR: Could not allocate space for netlink header\n"); |
| goto done; |
| } |
| memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD)); |
| /* Fill the netlink message header */ |
| nlh->nlmsg_len = NLMSG_SPACE(NL_MAX_PAYLOAD); |
| nlh->nlmsg_pid = getpid(); /* self pid */ |
| nlh->nlmsg_flags = 0; |
| |
| /* Initialize I/O vector */ |
| memset(&iov, 0, sizeof(struct iovec)); |
| iov.iov_base = (void *)nlh; |
| iov.iov_len = nlh->nlmsg_len; |
| |
| /* Set destination address */ |
| memset(&dest_addr, 0, sizeof(struct sockaddr_nl)); |
| dest_addr.nl_family = AF_NETLINK; |
| dest_addr.nl_pid = 0; /* Kernel */ |
| dest_addr.nl_groups = NL_MULTICAST_GROUP; |
| |
| /* Initialize message header */ |
| memset(&msg, 0, sizeof(struct msghdr)); |
| msg.msg_name = (void *)&dest_addr; |
| msg.msg_namelen = sizeof(dest_addr); |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| |
| while (!terminate_flag) { |
| /* event buffer is received for all the interfaces */ |
| bytes_read = read_event(nl_sk, &msg, &tv); |
| /* handle only NETLINK events here */ |
| drv_nlevt_handler((struct nlmsghdr *)nlh, bytes_read, |
| &evt_conn); |
| if (assoc_flag && is_ht_ap) { |
| /** If the event is connected with an HT AP then issue OBSS scan immediately */ |
| if (evt_conn) { |
| /** Issue OBSS scan */ |
| process_setuserscan(); |
| /** Invoke 2040coex command, if any legacy AP found or |
| * any AP has 40MHz intolarent bit set */ |
| if (coex_cmd_req_flag) |
| invoke_coex_command(); |
| } |
| tv.tv_sec = |
| (t_u32)le16_to_cpu(cur_obss_scan_param. |
| bss_chan_width_trigger_scan_int); |
| } else { |
| tv.tv_sec = DEFAULT_SCAN_INTERVAL; |
| } |
| tv.tv_usec = 0; |
| } |
| |
| done: |
| if (nl_sk > 0) |
| close(nl_sk); |
| if (nlh) |
| free(nlh); |
| return; |
| } |
| |
| /** |
| * @brief Determine the netlink number |
| * |
| * @return Netlink number to use |
| */ |
| static int |
| get_netlink_num(int dev_index) |
| { |
| FILE *fp = NULL; |
| int netlink_num = NETLINK_MARVELL; |
| char str[64]; |
| char *srch = "netlink_num"; |
| char filename[64]; |
| |
| if (dev_index == 0) { |
| strcpy(filename, "/proc/mwlan/config"); |
| } else if (dev_index > 0) { |
| sprintf(filename, "/proc/mwlan/config%d", dev_index); |
| } |
| /* Try to open /proc/mwlan/config$ */ |
| fp = fopen(filename, "r"); |
| |
| if (fp) { |
| while (!feof(fp)) { |
| fgets(str, sizeof(str), fp); |
| if (strncmp(str, srch, strlen(srch)) == 0) { |
| netlink_num = atoi(str + strlen(srch) + 1); |
| break; |
| } |
| } |
| fclose(fp); |
| } else { |
| return -1; |
| } |
| |
| printf("Netlink number = %d\n", netlink_num); |
| return netlink_num; |
| } |
| |
| /** |
| * @brief opens netlink socket to receive NETLINK events |
| * @return socket id --success, otherwise--MLAN_STATUS_FAILURE |
| */ |
| int |
| open_netlink(int dev_index) |
| { |
| int sk = -1; |
| struct sockaddr_nl src_addr; |
| int netlink_num = 0; |
| |
| netlink_num = get_netlink_num(dev_index); |
| if (netlink_num < 0) { |
| printf("ERR:Could not get netlink socket. Invalid device number.\n"); |
| return sk; |
| } |
| |
| /* Open netlink socket */ |
| sk = socket(PF_NETLINK, SOCK_RAW, netlink_num); |
| if (sk < 0) { |
| printf("ERR:Could not open netlink socket.\n"); |
| return sk; |
| } |
| |
| /* Set source address */ |
| memset(&src_addr, 0, sizeof(src_addr)); |
| src_addr.nl_family = AF_NETLINK; |
| src_addr.nl_pid = getpid(); |
| src_addr.nl_groups = NL_MULTICAST_GROUP; |
| |
| /* Bind socket with source address */ |
| if (bind(sk, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) { |
| printf("ERR:Could not bind socket!\n"); |
| close(sk); |
| return -1; |
| } |
| return sk; |
| } |
| |
| /** |
| * @brief Terminate signal handler |
| * @param signal Signal to handle |
| * @return NA |
| */ |
| static t_void |
| terminate_handler(int signal) |
| { |
| printf("Stopping application.\n"); |
| #if DEBUG |
| printf("Process ID of process killed = %d\n", getpid()); |
| #endif |
| terminate_flag = TRUE; |
| } |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| /** |
| * @brief Process host command |
| * @param hostcmd_idx Host command index |
| * @param chan_list A pointer to a channel list |
| * @param chan_num Number of channels in the channel list |
| * @param reg_class Regulatory class of the channels |
| * @param is_intol_ap_present Flag:is there any 40 MHz intolerant AP is present or not |
| * |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| int |
| process_host_cmd(int hostcmd_idx, t_u8 *chan_list, t_u8 chan_num, |
| t_u8 reg_class, t_u8 is_intol_ap_present) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| t_u8 *buffer = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| prepare_buffer(buffer, "hostcmd", 0, NULL); |
| switch (hostcmd_idx) { |
| case CMD_2040COEX: |
| prepare_coex_cmd_buff(buffer + strlen(CMD_MARVELL) + |
| strlen("hostcmd"), chan_list, chan_num, |
| reg_class, is_intol_ap_present); |
| break; |
| default: |
| break; |
| } |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| perror("mlan2040coex"); |
| fprintf(stderr, "mlan2040coex: hostcmd fail\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| ret = process_host_cmd_resp("hostcmd", buffer); |
| |
| done: |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| return ret; |
| } |
| |
| /** |
| * @brief Check the STA is 40 MHz intolerant or not |
| * @param intol Flag: TRUE when the STA is 40 MHz intolerant, otherwise FALSE |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| int |
| is_intolerant_sta(int *intol) |
| { |
| t_u8 *buffer = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| int htcap_info, ret = MLAN_STATUS_SUCCESS; |
| |
| *intol = FALSE; |
| |
| /* Initialize buffer */ |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* buffer = CMD_MARVELL + <cmd_string> */ |
| prepare_buffer(buffer, "htcapinfo", 0, NULL); |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| perror("mlan2040coex"); |
| fprintf(stderr, "mlan2040coex: htcapinfo fail\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| htcap_info = *((int *)(buffer)); |
| |
| if (htcap_info & MBIT(8)) |
| *intol = TRUE; |
| |
| done: |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| return ret; |
| } |
| |
| /** |
| * @brief get region code |
| * @param reg_code Pointer to region code |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| int |
| get_region_code(int *reg_code) |
| { |
| t_u8 *buffer = NULL; |
| struct eth_priv_cmd *cmd = NULL; |
| struct ifreq ifr; |
| int ret = MLAN_STATUS_SUCCESS; |
| |
| /* Initialize buffer */ |
| buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); |
| if (!buffer) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* buffer = CMD_MARVELL + <cmd_string> */ |
| prepare_buffer(buffer, "regioncode", 0, NULL); |
| |
| cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); |
| if (!cmd) { |
| printf("ERR:Cannot allocate buffer for command!\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Fill up buffer */ |
| #ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT |
| memset(cmd, 0, sizeof(struct eth_priv_cmd)); |
| memcpy(&cmd->buf, &buffer, sizeof(buffer)); |
| #else |
| cmd->buf = buffer; |
| #endif |
| cmd->used_len = 0; |
| cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; |
| |
| /* Perform IOCTL */ |
| memset(&ifr, 0, sizeof(struct ifreq)); |
| strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); |
| ifr.ifr_ifru.ifru_data = (void *)cmd; |
| |
| if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { |
| perror("mlan2040coex"); |
| fprintf(stderr, "mlan2040coex: regioncode fail\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| memcpy(reg_code, buffer, sizeof(int)); |
| done: |
| if (cmd) |
| free(cmd); |
| if (buffer) |
| free(buffer); |
| return ret; |
| } |
| |
| /** No option */ |
| #define NO_OPTION -1 |
| |
| /** |
| * @brief Entry function for coex |
| * @param argc number of arguments |
| * @param argv A pointer to arguments array |
| * @return MLAN_STATUS_SUCCESS--success, otherwise--fail |
| */ |
| int |
| main(int argc, char *argv[]) |
| { |
| char ifname[IFNAMSIZ + 1] = "mlan0"; |
| int c, daemonize = FALSE; |
| t_s32 nl_sk; |
| /**< netlink socket descriptor to receive an event */ |
| int dev_index = 0; /** initialise with -1 to open multiple NETLINK Sockets */ |
| |
| char temp[2]; |
| int arg_count = 0; |
| int ifname_given = FALSE; |
| |
| for (;;) { |
| c = getopt(argc, argv, "Bhi:vd:"); |
| /* check if all command-line options have been parsed */ |
| if (c == NO_OPTION) |
| break; |
| |
| switch (c) { |
| case 'B': |
| daemonize = TRUE; |
| break; |
| case 'h': |
| display_usage(); |
| return MLAN_STATUS_SUCCESS; |
| case 'v': |
| fprintf(stdout, |
| "Marvell 20/40coex application version %s\n", |
| COEX_VER); |
| return MLAN_STATUS_SUCCESS; |
| case 'i': |
| ifname_given = TRUE; |
| if (strlen(optarg) < IFNAMSIZ) |
| strncpy(ifname, optarg, IFNAMSIZ - 1); |
| else { |
| fprintf(stdout, "Interface name too long\n"); |
| display_usage(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| arg_count += 1; |
| break; |
| case 'd': |
| strncpy(temp, optarg, strlen(optarg)); |
| if (isdigit(temp[0])) |
| dev_index = atoi(temp); |
| arg_count += 1; |
| break; |
| default: |
| fprintf(stdout, "Invalid argument\n"); |
| display_usage(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| } |
| |
| if (!ifname_given) { |
| sprintf(ifname, "mlan%d", dev_index); |
| } |
| |
| if (optind < argc) { |
| fputs("Too many arguments.\n", stderr); |
| display_usage(); |
| goto done; |
| } |
| |
| strncpy(dev_name, ifname, IFNAMSIZ - 1); |
| |
| /* create a socket */ |
| if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
| fprintf(stderr, "mlan2040coex: Cannot open socket.\n"); |
| goto done; |
| } |
| |
| /* create netlink sockets and bind them with app side addr */ |
| nl_sk = open_netlink(dev_index); |
| |
| if (nl_sk < 0) { |
| fprintf(stderr, "mlan2040coex: Cannot open netlink socket.\n"); |
| goto done; |
| } |
| |
| signal(SIGHUP, terminate_handler); /* catch hangup signal */ |
| signal(SIGTERM, terminate_handler); /* catch kill signal */ |
| signal(SIGINT, terminate_handler); /* catch kill signal */ |
| signal(SIGALRM, terminate_handler); /* catch kill signal */ |
| |
| /** Make the process background-process */ |
| if (daemonize) { |
| if (daemon(0, 0)) |
| fprintf(stderr, "mlan2040coex: Cannot start daemon\n"); |
| } |
| |
| /** run the application */ |
| run_app(nl_sk); |
| |
| done: |
| if (sockfd > 0) |
| close(sockfd); |
| if (nl_sk > 0) |
| close(nl_sk); |
| |
| return MLAN_STATUS_SUCCESS; |
| } |