diff options
Diffstat (limited to 'drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_main.c')
-rw-r--r-- | drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_main.c | 9625 |
1 files changed, 9625 insertions, 0 deletions
diff --git a/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_main.c b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_main.c new file mode 100644 index 000000000000..2bd60785c8cb --- /dev/null +++ b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_main.c @@ -0,0 +1,9625 @@ +/** @file moal_main.c + * + * @brief This file contains the major functions in WLAN + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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: + 10/21/2008: initial version +********************************************************/ + +#include "moal_main.h" +#ifdef USB +#include "moal_usb.h" +#endif +#ifdef SDIO +#include "moal_sdio.h" +#endif +#ifdef PCIE +#include "moal_pcie.h" +#endif +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#include "moal_cfg80211_util.h" +#endif +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +#include "moal_sta_cfg80211.h" +#endif +#endif +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +#include "moal_uap_cfg80211.h" +#endif +#endif +#include "moal_eth_ioctl.h" + +#include <linux/if_ether.h> +#include <linux/in.h> +#include <linux/tcp.h> +#include <net/tcp.h> +#include <net/dsfield.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#endif + +/******************************************************** + Global Variables + ********************************************************/ +/** the pointer of new fwdump fname for each dump**/ +char *fwdump_fname; +/** Semaphore for add/remove card */ +struct semaphore AddRemoveCardSem; +/** + * The global variable of a pointer to moal_handle + * structure variable + **/ +moal_handle *m_handle[MAX_MLAN_ADAPTER]; +/** Global veriable for usb independent reset */ +extern int fw_reload; + +extern int wifi_status; + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int fw_region; +#endif +#endif +/******************************************************** + Local Variables +********************************************************/ + +#ifdef SD8887 +static struct _card_info card_info_SD8887 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 1, + .low_power_enable = 1, + .rx_rate_max = 196, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .rev_id_reg = 0xc8, + .fw_name = SD8887_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8887_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V2, + .dump_fw_ctrl_reg = 0xa2, + .dump_fw_start_reg = 0xa3, + .dump_fw_end_reg = 0xaa, + .dump_fw_host_ready = 0xee, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0x90, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0B6, + .fw_reset_val = 1, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef SD8897 +static struct _card_info card_info_SD8897 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xbc, + .fw_name = SD8897_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8897_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V2, + .dump_fw_ctrl_reg = 0xe2, + .dump_fw_start_reg = 0xe3, + .dump_fw_end_reg = 0xea, + .dump_fw_host_ready = 0xee, + .dump_reg.reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, 0x59, 0x5c, 0x5d}, + .dump_reg.reg_table_size = 8, + .scratch_reg = 0xc0, + .func1_reg_start = 0x04, + .func1_reg_end = 0x0b, + .fw_reset_reg = 0x0E8, + .fw_reset_val = 1, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef PCIE8897 +static struct _card_info card_info_PCIE8897 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x0c58, + .fw_name = PCIE8897_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE8897_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef USB8897 +static struct _card_info card_info_USB8897 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT, + .fw_name = USB8897_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB8897_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef SD8977 +static struct _card_info card_info_SD8977 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 76, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD8977_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8977_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD8978 +static struct _card_info card_info_SD8978 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 76, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD8978_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8978_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD8997 +static struct _card_info card_info_SD8997 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD8997_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8997_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD9098 +static struct _card_info card_info_SD9098 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD9098_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD9098_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x90002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD9097 +static struct _card_info card_info_SD9097 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD9097_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD9097_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x90002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef PCIE8997 +static struct _card_info card_info_PCIE8997 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x8, + .host_strap_reg = 0x0cd0, + .magic_reg = 0x0cd4, + .fw_name = PCIE8997_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE8997_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef PCIE9097 +static struct _card_info card_info_PCIE9097 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x8, + .host_strap_reg = 0x1c70, + .magic_reg = 0x1c74, + .fw_name = PCIE9097_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE9097_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef PCIE9098 +static struct _card_info card_info_PCIE9098 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x8, + .host_strap_reg = 0x1c70, + .magic_reg = 0x1c74, + .fw_name = PCIE9098_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE9098_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB8978 +static struct _card_info card_info_USB8978 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 76, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .histogram_table_num = 1, + .fw_name = USB8978_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB8978_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB8997 +static struct _card_info card_info_USB8997 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 196, + .feature_control = FEATURE_CTRL_DEFAULT, + .histogram_table_num = 3, + .fw_name = USB8997_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB8997_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB9098 +static struct _card_info card_info_USB9098 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .feature_control = FEATURE_CTRL_DEFAULT, + .histogram_table_num = 3, + .fw_name = USB9098_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB9098_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB9097 +static struct _card_info card_info_USB9097 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .feature_control = FEATURE_CTRL_DEFAULT, + .histogram_table_num = 3, + .fw_name = USBUSB9097_COMBO_V1_FW_NAME, + .fw_name_wlan = USB9097_WLAN_V1_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif +#ifdef SD8987 +static struct _card_info card_info_SD8987 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 0, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 1, + .rx_rate_max = 196, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .histogram_table_num = 3, + .fw_name = SD8987_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8987_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +/** Driver version */ +char driver_version[] = + INTF_CARDTYPE KERN_VERSION "--" MLAN_RELEASE_VERSION "-GPL" + "-(" + "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** woal_callbacks */ +static mlan_callbacks woal_callbacks = { + .moal_get_fw_data = moal_get_fw_data, + .moal_get_vdll_data = moal_get_vdll_data, + .moal_get_hw_spec_complete = moal_get_hw_spec_complete, + .moal_init_fw_complete = moal_init_fw_complete, + .moal_shutdown_fw_complete = moal_shutdown_fw_complete, + .moal_send_packet_complete = moal_send_packet_complete, + .moal_recv_packet = moal_recv_packet, + .moal_recv_event = moal_recv_event, + .moal_ioctl_complete = moal_ioctl_complete, + .moal_alloc_mlan_buffer = moal_alloc_mlan_buffer, + .moal_free_mlan_buffer = moal_free_mlan_buffer, +#ifdef USB + .moal_recv_complete = moal_recv_complete, + .moal_write_data_async = moal_write_data_async, +#endif /* USB */ + +#if defined(SDIO) || defined(PCIE) + .moal_write_reg = moal_write_reg, + .moal_read_reg = moal_read_reg, +#endif /* SDIO || PCIE */ + .moal_write_data_sync = moal_write_data_sync, + .moal_read_data_sync = moal_read_data_sync, + .moal_malloc = moal_malloc, + .moal_mfree = moal_mfree, + .moal_vmalloc = moal_vmalloc, + .moal_vfree = moal_vfree, +#ifdef PCIE + .moal_malloc_consistent = moal_malloc_consistent, + .moal_mfree_consistent = moal_mfree_consistent, + .moal_map_memory = moal_map_memory, + .moal_unmap_memory = moal_unmap_memory, +#endif /* PCIE */ + .moal_memset = moal_memset, + .moal_memcpy = moal_memcpy, + .moal_memcpy_ext = moal_memcpy_ext, + .moal_memmove = moal_memmove, + .moal_memcmp = moal_memcmp, + .moal_udelay = moal_udelay, + .moal_usleep_range = moal_usleep_range, + .moal_get_system_time = moal_get_system_time, + .moal_get_boot_ktime = moal_get_boot_ktime, + .moal_init_timer = moal_init_timer, + .moal_free_timer = moal_free_timer, + .moal_start_timer = moal_start_timer, + .moal_stop_timer = moal_stop_timer, + .moal_init_lock = moal_init_lock, + .moal_free_lock = moal_free_lock, + .moal_spin_lock = moal_spin_lock, + .moal_spin_unlock = moal_spin_unlock, + .moal_print = moal_print, + .moal_print_netintf = moal_print_netintf, + .moal_assert = moal_assert, + .moal_hist_data_add = moal_hist_data_add, +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + .moal_wait_hostcmd_complete = moal_wait_hostcmd_complete, + .moal_notify_hostcmd_complete = moal_notify_hostcmd_complete, +#endif + .moal_tp_accounting = moal_tp_accounting, + .moal_tp_accounting_rx_param = moal_tp_accounting_rx_param, +}; + +int woal_open(struct net_device *dev); +int woal_close(struct net_device *dev); +int woal_set_mac_address(struct net_device *dev, void *addr); +void woal_tx_timeout(struct net_device *dev +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + , + unsigned int txqueue +#endif +); +struct net_device_stats *woal_get_stats(struct net_device *dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev); +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev, + select_queue_fallback_t fallback); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb); +#endif +#endif + +static moal_handle *reset_handle; +/** Hang workqueue */ +static struct workqueue_struct *hang_workqueue; +/** Hang work */ +static struct work_struct hang_work; + +/** + * @brief This function process FW hang + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +static void woal_hang_work_queue(struct work_struct *work) +{ + int i; + ENTER(); + if (!reset_handle) { + LEAVE(); + return; + } + for (i = 0; i < reset_handle->priv_num; i++) { + if (reset_handle->priv[i] && reset_handle->priv[i]->netdev) { + PRINTM(MMSG, "Close netdev %s\n", + reset_handle->priv[i]->netdev->name); + rtnl_lock(); + dev_close(reset_handle->priv[i]->netdev); + rtnl_unlock(); + break; + } + } + reset_handle = NULL; + LEAVE(); +} + +/** + * @brief This function process FW hang + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +void woal_process_hang(moal_handle *handle) +{ + ENTER(); + if (reset_handle == NULL) { + PRINTM(MMSG, "Start to process hanging\n"); + reset_handle = handle; + mlan_ioctl(handle->pmlan_adapter, NULL); + queue_work(hang_workqueue, &hang_work); +#ifdef ANDROID_KERNEL +#define WAKE_LOCK_HANG 5000 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&reset_handle->ws, WAKE_LOCK_HANG); +#else + wake_lock_timeout(&reset_handle->wake_lock, + msecs_to_jiffies(WAKE_LOCK_HANG)); +#endif +#endif + } + LEAVE(); +} + +/** + * @brief Check if any interface is active + * + * @param handle A pointer to moal_handle + * + * + * @return MTRUE/MFALSE; + */ +t_u8 woal_is_any_interface_active(moal_handle *handle) +{ + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) + return MTRUE; + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if (handle->priv[i]->bss_started == MTRUE) + return MTRUE; + } +#endif + } + return MFALSE; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return MTRUE or MFALSE + */ +BOOLEAN woal_ssid_valid(mlan_802_11_ssid *pssid) +{ +#ifdef ASCII_SSID_CHECK + unsigned int ssid_idx; + + ENTER(); + + for (ssid_idx = 0; ssid_idx < pssid->ssid_len; ssid_idx++) { + if ((pssid->ssid[ssid_idx] < 0x20) || + (pssid->ssid[ssid_idx] > 0x7e)) { + LEAVE(); + return MFALSE; + } + } + LEAVE(); +#endif + return MTRUE; +} + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief Remain on Channel timeout function + * + * @param context A pointer to context + * @return N/A + */ +void woal_remain_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + moal_private *priv = handle->priv[handle->remain_bss_index]; + + ENTER(); + + PRINTM(MEVENT, "remain_timer fired.\n"); + if (handle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + priv->netdev, +#else + priv->wdev, +#endif + handle->cookie, &handle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + handle->channel_type, +#endif + GFP_ATOMIC); + handle->cookie = 0; + } + handle->is_remain_timer_set = MFALSE; + + LEAVE(); + return; +} +#endif +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** + * @brief GO timeout function + * + * @param context A pointer to context + * @return N/A + */ +void woal_go_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + + ENTER(); + + PRINTM(MEVENT, "go_timer fired.\n"); + handle->is_go_timer_set = MFALSE; + + LEAVE(); + return; +} + +#endif +#endif + +/** + * @brief check if we already connect to the AP. + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MTRUE/MFALSE; + */ +int woal_is_connected(moal_private *priv, mlan_ssid_bssid *ssid_bssid) +{ + mlan_bss_info bss_info; + int ret = MFALSE; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + ENTER(); + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) + goto done; + if (bss_info.media_connected) { + if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) { + if (ssid_bssid->ssid.ssid_len) { /* compare ssid and + bssid */ + if ((ssid_bssid->ssid.ssid_len == + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->ssid.ssid, + bss_info.ssid.ssid, + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->bssid, bss_info.bssid, + MLAN_MAC_ADDR_LENGTH)) + ret = MTRUE; + } else { /* compare bssid */ + if (!memcmp(ssid_bssid->bssid, bss_info.bssid, + MLAN_MAC_ADDR_LENGTH)) { + moal_memcpy_ext( + priv->phandle, + &ssid_bssid->ssid, + &bss_info.ssid, + sizeof(bss_info.ssid), + sizeof(ssid_bssid->ssid)); + ret = MTRUE; + } + } + } else { /* compare ssid */ + if (ssid_bssid->ssid.ssid_len && + (ssid_bssid->ssid.ssid_len == + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->ssid.ssid, bss_info.ssid.ssid, + bss_info.ssid.ssid_len)) { + moal_memcpy_ext(priv->phandle, + &ssid_bssid->bssid, + &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(ssid_bssid->bssid)); + ret = MTRUE; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Look up specific IE in a buf + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param id Element id to lookup + * + * @return Pointer of the specific IE -- success, NULL -- fail + */ +const t_u8 *woal_parse_ie_tlv(const t_u8 *ie, int len, t_u8 id) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + + /* IE format: + * | u8 | id | + * | u8 | len | + * | var | data | + */ + while (left_len >= 2) { + length = *(pos + 1); + if ((*pos == id) && (length + 2) <= left_len) + return pos; + pos += (length + 2); + left_len -= (length + 2); + } + + return NULL; +} + +/** + * @brief Look up specific IE in Extension IE + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ext_id Extended Element id to lookup + * + * @return Pointer of the specific Extended IE -- success, NULL + * -- fail + */ +const t_u8 *woal_parse_ext_ie_tlv(const t_u8 *ie, int len, t_u8 ext_id) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + + /* Extension IE format: + * | u8 | id | + * | u8 | len | + * | u8 | ext_id | + * | var | data | + */ + while (left_len >= 2) { + length = *(pos + 1); + if ((*pos == EXTENSION) && (length + 2) <= left_len) { + if (*(pos + 2) == ext_id) + return pos; + } + pos += (length + 2); + left_len -= (length + 2); + } + return NULL; +} + +/** + * @brief Get mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT) + * + * @return Wireless mode + */ +t_u32 woal_get_mode(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mode = 0; + + ENTER(); + +#if defined(STA_WEXT) || defined(UAP_WEXT) + mode = priv->w_stats.status; +#endif + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + switch (bss->param.bss_mode) { + case MLAN_BSS_MODE_INFRA: + mode = MW_MODE_INFRA; + break; + case MLAN_BSS_MODE_IBSS: + mode = MW_MODE_ADHOC; + break; + default: + mode = MW_MODE_AUTO; + break; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return mode; +} + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function update the default firmware name + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +void woal_update_firmware_name(moal_handle *handle) +{ + if (handle->params.fw_name) { + handle->drv_mode.fw_name = handle->params.fw_name; + } else { + if (!moal_extflg_isset(handle, EXT_FW_SERIAL) || + handle->fw_reload || handle->params.fw_reload) { + handle->drv_mode.fw_name = + handle->card_info->fw_name_wlan; + } else + handle->drv_mode.fw_name = handle->card_info->fw_name; + } +} +/** + * @brief This function dynamically populates the driver mode table + * + * @param handle A pointer to moal_handle structure + * @param drv_mode_local Driver mode + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_update_drv_tbl(moal_handle *handle, int drv_mode_local) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + unsigned int intf_num = 0; + int i = 0, j = 0; + mlan_bss_attr *bss_tbl = NULL; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + int last_wfd_index = 0; + int max_vir_bss = handle->params.max_vir_bss; +#endif +#endif +#ifdef STA_SUPPORT + int max_sta_bss = handle->params.max_sta_bss; +#endif +#ifdef UAP_SUPPORT + int max_uap_bss = handle->params.max_uap_bss; +#endif +#ifdef WIFI_DIRECT_SUPPORT + int max_wfd_bss = handle->params.max_wfd_bss; +#endif + + ENTER(); + + /* Calculate number of interfaces */ +#ifdef STA_SUPPORT + if (drv_mode_local & DRV_MODE_STA) { + if ((max_sta_bss < 1) || (max_sta_bss > MAX_STA_BSS)) { + PRINTM(MWARN, + "Unsupported max_sta_bss (%d), setting to default\n", + max_sta_bss); + max_sta_bss = DEF_STA_BSS; + } + intf_num += max_sta_bss; + handle->params.max_sta_bss = max_sta_bss; + } +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT + if (drv_mode_local & DRV_MODE_UAP) { + if ((max_uap_bss < 1) || (max_uap_bss > MAX_UAP_BSS)) { + PRINTM(MWARN, + "Unsupported max_uap_bss (%d), setting to default\n", + max_uap_bss); + max_uap_bss = DEF_UAP_BSS; + } + intf_num += max_uap_bss; + handle->params.max_uap_bss = max_uap_bss; + } +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT + if (drv_mode_local & DRV_MODE_WIFIDIRECT) { + if ((max_wfd_bss < 1) || (max_wfd_bss > MAX_WIFIDIRECT_BSS)) { + PRINTM(MWARN, + "Unsupported max_wfd_bss (%d), setting to default\n", + max_wfd_bss); + max_wfd_bss = DEF_WIFIDIRECT_BSS; + } + intf_num += max_wfd_bss; + handle->params.max_wfd_bss = max_wfd_bss; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + intf_num += max_vir_bss; +#endif + } + +#endif /* WIFI_DIRECT_SUPPORT */ + + /* Create BSS attribute table */ + if ((intf_num == 0) || (intf_num > MLAN_MAX_BSS_NUM)) { + PRINTM(MERROR, "Unsupported number of BSS %d\n", intf_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + /* Create new table */ + bss_tbl = kmalloc(sizeof(mlan_bss_attr) * intf_num, GFP_KERNEL); + if (!bss_tbl) { + PRINTM(MERROR, + "Could not create BSS attribute table\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Populate BSS attribute table */ +#ifdef STA_SUPPORT + if (drv_mode_local & DRV_MODE_STA) { + for (j = 0; j < max_sta_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_STA; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT + if (drv_mode_local & DRV_MODE_UAP) { + for (j = 0; j < max_uap_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_UAP; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT + if (drv_mode_local & DRV_MODE_WIFIDIRECT) { + for (j = 0; j < max_wfd_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + last_wfd_index = j; +#endif + } +#endif /* WIFI_DIRECT_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + /** append virtual interface at the end of table */ + for (j = 0; j < max_vir_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j + last_wfd_index; + bss_tbl[i].bss_virtual = MTRUE; + i++; + } +#endif +#endif + + /* Clear existing table, if any */ + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + + /* Create moal_drv_mode entry */ + handle->drv_mode.drv_mode = handle->params.drv_mode; + handle->drv_mode.intf_num = intf_num; + handle->drv_mode.bss_attr = bss_tbl; + + /* update default firmware name */ + woal_update_firmware_name(handle); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function initializes software + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_init_sw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + unsigned int i; + mlan_device device; + t_void *pmlan; + int cfg80211_wext = handle->params.cfg80211_wext; + + ENTER(); + + /* Initialize moal_handle structure */ + handle->hardware_status = HardwareStatusInitializing; + handle->main_state = MOAL_STATE_IDLE; + +#ifdef STA_SUPPORT + if ((handle->params.drv_mode & DRV_MODE_STA) +#ifdef STA_WEXT + && !IS_STA_WEXT(cfg80211_wext) +#endif +#ifdef STA_CFG80211 + && !IS_STA_CFG80211(cfg80211_wext) +#endif +#ifdef MFG_CMD_SUPPORT + && !handle->params.mfg_mode +#endif + ) { + PRINTM(MERROR, + "STA without WEXT or CFG80211 bit definition!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif /* STA_SUPPORT */ + +#if defined(STA_CFG80211) && defined(STA_SUPPORT) + if (IS_STA_CFG80211(cfg80211_wext)) + cfg80211_wext |= STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + +#if defined(UAP_CFG80211) && defined(UAP_SUPPORT) + if (IS_UAP_CFG80211(cfg80211_wext)) + cfg80211_wext |= STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + handle->params.cfg80211_wext = cfg80211_wext; + moal_memcpy_ext(handle, handle->driver_version, driver_version, + strlen(driver_version), MLAN_MAX_VER_STR_LEN - 1); + + if (woal_update_drv_tbl(handle, handle->params.drv_mode) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /** user config file */ + init_waitqueue_head(&handle->init_user_conf_wait_q); + + /* PnP and power profile */ + handle->surprise_removed = MFALSE; + init_waitqueue_head(&handle->init_wait_q); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + spin_lock_init(&handle->queue_lock); +#endif + spin_lock_init(&handle->driver_lock); + spin_lock_init(&handle->ioctl_lock); + spin_lock_init(&handle->scan_req_lock); + + handle->is_suspended = MFALSE; + handle->hs_activated = MFALSE; + handle->hs_auto_arp = MTRUE; + handle->suspend_fail = MFALSE; + handle->hs_skip_count = 0; + handle->hs_force_count = 0; +#ifdef SDIO + if (IS_SD(handle->card_type)) { +#ifdef SDIO_SUSPEND_RESUME + handle->suspend_notify_req = MFALSE; +#endif + handle->cmd52_func = 0; + handle->cmd52_reg = 0; + handle->cmd52_val = 0; + } +#endif + + if (handle->params.scan_chan_gap && + (handle->params.scan_chan_gap <= MRVDRV_MAX_SCAN_CHAN_GAP_TIME)) + handle->scan_chan_gap = handle->params.scan_chan_gap; + else + handle->scan_chan_gap = DEF_SCAN_CHAN_GAP; + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT + handle->miracast_scan_time = DEF_MIRACAST_SCAN_TIME; +#define DEF_NOA_DURATION 0 +#define DEF_NOA_INTERVAL 100 + handle->noa_duration = DEF_NOA_DURATION; + handle->noa_interval = DEF_NOA_INTERVAL; +#endif +#endif + + if (IS_USB(handle->card_type)) + init_waitqueue_head(&handle->suspend_wait_q); + init_waitqueue_head(&handle->hs_activate_wait_q); + + /* Initialize measurement wait queue */ + handle->meas_wait_q_woken = MFALSE; + handle->meas_start_jiffies = 0; + handle->cac_period = MFALSE; + handle->delay_bss_start = MFALSE; + init_waitqueue_head(&handle->meas_wait_q); +#if defined(UAP_SUPPORT) + handle->chsw_wait_q_woken = MFALSE; + init_waitqueue_head(&handle->chsw_wait_q); +#endif + + handle->cac_period_jiffies = 0; + handle->usr_nop_period_sec = 0; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&handle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); + woal_initialize_timer(&handle->cac_timer, woal_cac_timer_func, handle); + handle->is_cac_timer_set = MFALSE; + handle->cac_bss_index = 0xff; +#endif +#endif + +#ifdef REASSOCIATION + MOAL_INIT_SEMAPHORE(&handle->reassoc_sem); + handle->reassoc_on = 0; + + /* Initialize the timer for the reassociation */ + woal_initialize_timer(&handle->reassoc_timer, woal_reassoc_timer_func, + handle); + + handle->is_reassoc_timer_set = MFALSE; +#endif /* REASSOCIATION */ + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + /* Initialize the timer for GO timeout */ + woal_initialize_timer(&handle->go_timer, woal_go_timer_func, handle); + + handle->is_go_timer_set = MFALSE; +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + handle->remain_on_channel = MFALSE; + + /* Initialize the timer for remain on channel */ + woal_initialize_timer(&handle->remain_timer, woal_remain_timer_func, + handle); + + handle->is_remain_timer_set = MFALSE; +#endif +#endif + + /* Register to MLAN */ + memset(&device, 0, sizeof(mlan_device)); + device.pmoal_handle = handle; + device.card_type = handle->card_type; + device.card_rev = handle->card_rev; +#ifdef USB + if (IS_USB(handle->card_type)) { + struct usb_card_rec *cardp = handle->card; + device.tx_cmd_ep = cardp->tx_cmd_ep; + device.rx_cmd_ep = cardp->rx_cmd_ep; + device.tx_data_ep = cardp->tx_data_ep; + device.rx_data_ep = cardp->rx_data_ep; + } +#endif +#ifdef MFG_CMD_SUPPORT + device.mfg_mode = (t_u32)handle->params.mfg_mode; +#endif +#ifdef DEBUG_LEVEL1 + device.drvdbg = drvdbg; +#endif + device.fixed_beacon_buffer = + (t_u32)moal_extflg_isset(handle, EXT_FIX_BCN_BUF); + device.auto_ds = (t_u32)handle->params.auto_ds; + device.ps_mode = (t_u32)handle->params.ps_mode; + device.passive_to_active_scan = (t_u8)handle->params.p2a_scan; + device.max_tx_buf = (t_u32)handle->params.max_tx_buf; +#if defined(STA_SUPPORT) + device.cfg_11d = (t_u32)handle->params.cfg_11d; +#endif + device.indrstcfg = (t_u32)handle->params.indrstcfg; +#ifdef SDIO + if (IS_SD(handle->card_type)) { + device.sdio_rx_aggr_enable = + moal_extflg_isset(handle, EXT_SDIO_RX_AGGR); + device.int_mode = (t_u32)moal_extflg_isset(handle, EXT_INTMODE); + device.gpio_pin = (t_u32)handle->params.gpiopin; +#ifdef SDIO_MMC +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + device.max_segs = ((struct sdio_mmc_card *)handle->card) + ->func->card->host->max_segs; + device.max_seg_size = ((struct sdio_mmc_card *)handle->card) + ->func->card->host->max_seg_size; +#endif + PRINTM(MMSG, "SDIO: max_segs=%d max_seg_size=%d\n", + device.max_segs, device.max_seg_size); +#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE + device.mpa_tx_cfg = MLAN_INIT_PARA_ENABLED; + device.mpa_rx_cfg = MLAN_INIT_PARA_ENABLED; +#else + device.mpa_tx_cfg = MLAN_INIT_PARA_DISABLED; + device.mpa_rx_cfg = MLAN_INIT_PARA_DISABLED; +#endif +#else + device.mpa_tx_cfg = MLAN_INIT_PARA_ENABLED; + device.mpa_rx_cfg = MLAN_INIT_PARA_ENABLED; +#endif /* SDIO_MMC */ + } +#endif /* SDIO */ + device.feature_control = handle->card_info->feature_control; + handle->feature_control = device.feature_control; + + if (handle->params.rx_work == MLAN_INIT_PARA_ENABLED) + device.rx_work = MTRUE; + else if (handle->params.rx_work == MLAN_INIT_PARA_DISABLED) + device.rx_work = MFALSE; + else { + if (num_possible_cpus() > 1) + device.rx_work = MTRUE; + else + device.rx_work = MFALSE; + } + PRINTM(MMSG, "rx_work=%d cpu_num=%d\n", device.rx_work, + num_possible_cpus()); + if (moal_extflg_isset(handle, EXT_NAPI)) + device.rx_work = MTRUE; + + device.dev_cap_mask = handle->params.dev_cap_mask; + + device.multi_dtim = handle->params.multi_dtim; + + device.inact_tmo = handle->params.inact_tmo; +#ifdef UAP_SUPPORT + device.uap_max_sta = handle->params.uap_max_sta; +#endif + device.hs_wake_interval = handle->params.hs_wake_interval; + device.indication_gpio = handle->params.indication_gpio; + device.hs_mimo_switch = moal_extflg_isset(handle, EXT_HS_MIMO_SWITCH); + + device.dfs53cfg = handle->params.dfs53cfg; + + for (i = 0; i < handle->drv_mode.intf_num; i++) { + device.bss_attr[i].bss_type = + handle->drv_mode.bss_attr[i].bss_type; + device.bss_attr[i].frame_type = + handle->drv_mode.bss_attr[i].frame_type; + device.bss_attr[i].active = handle->drv_mode.bss_attr[i].active; + device.bss_attr[i].bss_priority = + handle->drv_mode.bss_attr[i].bss_priority; + device.bss_attr[i].bss_num = + handle->drv_mode.bss_attr[i].bss_num; + device.bss_attr[i].bss_virtual = + handle->drv_mode.bss_attr[i].bss_virtual; + } + moal_memcpy_ext(handle, &device.callbacks, &woal_callbacks, + sizeof(mlan_callbacks), sizeof(mlan_callbacks)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + device.fw_region = fw_region; +#endif +#endif + device.drv_mode = handle->params.drv_mode; + if (MLAN_STATUS_SUCCESS == mlan_register(&device, &pmlan)) + handle->pmlan_adapter = pmlan; + else + ret = MLAN_STATUS_FAILURE; + + LEAVE(); + return ret; +} + +/** + * @brief This function frees the structure of moal_handle + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +void woal_free_moal_handle(moal_handle *handle) +{ + moal_handle *ref_handle = NULL; + + ENTER(); + if (!handle) { + PRINTM(MERROR, "The handle is NULL\n"); + LEAVE(); + return; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (handle->wiphy) { + wiphy_unregister(handle->wiphy); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + woal_cfg80211_free_iftype_data(handle->wiphy); +#endif + wiphy_free(handle->wiphy); + handle->wiphy = NULL; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) + if ((handle->nl_sk) && ((handle->nl_sk)->sk_socket)) { + sock_release((handle->nl_sk)->sk_socket); + handle->nl_sk = NULL; + } +#else + netlink_kernel_release(handle->nl_sk); +#endif + + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + handle->pmlan_adapter = NULL; + } + + /* Free BSS attribute table */ + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + PRINTM(MINFO, "Free Adapter\n"); + if (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d, malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } +#ifdef PCIE + if (IS_PCIE(handle->card_type) && + atomic_read(&handle->malloc_cons_count)) { + PRINTM(MERROR, "mlan has memory leak: malloc_cons_count=%d\n", + atomic_read(&handle->malloc_cons_count)); + } +#endif + + /* Free allocated memory for fwdump filename */ + kfree(handle->fwdump_fname); + if (fwdump_fname) { + kfree(fwdump_fname); + fwdump_fname = NULL; + } + /* Free module params */ + woal_free_module_param(handle); + /** clear pref_mac to avoid later crash */ + if (handle->pref_mac) { + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle->pref_mac && (ref_handle->pref_mac == handle)) + ref_handle->pref_mac = NULL; + } + /* Free the moal handle itself */ + kfree(handle); + LEAVE(); +} + +/** + * @brief WOAL get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return routnine status + */ +static t_size parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos) +{ + t_u8 *src, *dest; + static t_s32 pos; + + ENTER(); + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + LEAVE(); + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size && + *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + /* parse new line */ + pos++; + *dest = '\0'; + LEAVE(); + return strlen(line_pos); +} + +/** + * @brief Process register access request + * @param type_string String format Register type + * @param offset_string String format Register offset + * @param value_string String format Pointer to value + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_process_regrdwr(moal_handle *handle, t_u8 *type_string, + t_u8 *offset_string, t_u8 *value_string) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + int type, offset, value; + pmlan_ioctl_req ioctl_req = NULL; + mlan_ds_reg_mem *reg = NULL; + + ENTER(); + + /* Alloc ioctl_req */ + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + + if (ioctl_req == NULL) { + PRINTM(MERROR, "Can't alloc memory\n"); + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_atoi(&type, type_string)) + goto done; + if (MLAN_STATUS_SUCCESS != woal_atoi(&offset, offset_string)) + goto done; + if (MLAN_STATUS_SUCCESS != woal_atoi(&value, value_string)) + goto done; + + ioctl_req->req_id = MLAN_IOCTL_REG_MEM; + ioctl_req->action = MLAN_ACT_SET; + + reg = (mlan_ds_reg_mem *)ioctl_req->pbuf; + reg->sub_command = MLAN_OID_REG_RW; + if (type < 5) { + reg->param.reg_rw.type = type; + } else { + PRINTM(MERROR, "Unsupported Type\n"); + goto done; + } + reg->param.reg_rw.offset = offset; + reg->param.reg_rw.value = value; + + /* request ioctl for STA */ + ret = woal_request_ioctl(handle->priv[0], ioctl_req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + PRINTM(MINFO, "Register type: %d, offset: 0x%x, value: 0x%x\n", type, + offset, value); + ret = MLAN_STATUS_SUCCESS; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#if defined(SDIO) +/** + * @brief Read/Write registers value + * + * @param priv A pointer to moal_private structure + * @param action get / set action + * @param type type of register + * @param offset offset of register + * @param value value of registere + * + * @return 0 --success, otherwise fail + */ +static int woal_getset_regrdwr(moal_private *priv, t_u32 action, t_u32 type, + t_u32 offset, t_u32 *value) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_REG_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + req->action = action; + + reg_mem->param.reg_rw.type = type; + reg_mem->param.reg_rw.offset = offset; + if (req->action == MLAN_ACT_SET) + reg_mem->param.reg_rw.value = *value; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + *value = reg_mem->param.reg_rw.value; + PRINTM(MINFO, "woal_getset_regrdwr value=%x\n", *value); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set slew rate mode + * + * @param handle MOAL handle + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_set_sdio_slew_rate(moal_handle *handle) +{ + t_u32 value = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + t_u32 new_value = 0; + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) + return MLAN_STATUS_FAILURE; + + if ((handle->card_info->slew_rate_reg != 0) && + (handle->params.slew_rate > 3 || handle->params.slew_rate < 0)) + return MLAN_STATUS_FAILURE; + + ret = woal_getset_regrdwr(priv, MLAN_ACT_GET, MLAN_REG_MAC, + handle->card_info->slew_rate_reg, &value); + if (ret < 0) { + PRINTM(MERROR, "woal_getset_regrdwr get REG_MAC failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + new_value = value & ~(0x3 << handle->card_info->slew_rate_bit_offset); + new_value |= (t_u32)handle->params.slew_rate + << handle->card_info->slew_rate_bit_offset; + + if (value != new_value) { + PRINTM(MMSG, "Set REG 0x%8x: 0x%x slew_rate=%d\n", + handle->card_info->slew_rate_reg, new_value, + handle->params.slew_rate); + ret = woal_getset_regrdwr(priv, MLAN_ACT_SET, MLAN_REG_MAC, + handle->card_info->slew_rate_reg, + &new_value); + if (ret < 0) { + PRINTM(MERROR, + "woal_getset_regrdwr get REG_MAC failed\n"); + ret = MLAN_STATUS_FAILURE; + } + } +done: + return ret; +} +#endif /* SDIO */ + +#ifdef UAP_SUPPORT +/** + * @brief set uap operation contrl value + * + * @param handle MOAL handle + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_set_uap_operation_ctrl(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int uap_oper_ctrl; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_UAP); + if (!priv) { + PRINTM(MERROR, + "woal_set_uap_operation_ctrl failed, no uap interface\n"); + LEAVE(); + return ret; + } + uap_oper_ctrl = handle->params.uap_oper_ctrl; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_OPER_CTRL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + bss->param.ap_oper_ctrl.ctrl_value = + (t_u16)((uap_oper_ctrl & 0xffff0000) >> 16); + bss->param.ap_oper_ctrl.chan_opt = (t_u16)(uap_oper_ctrl & 0xffff); + PRINTM(MMSG, "Uap oper_ctrl=0x%x chan_opt=0x%x\n", + bss->param.ap_oper_ctrl.ctrl_value, + bss->param.ap_oper_ctrl.chan_opt); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief WOAL parse ASCII format data to MAC address + * + * @param handle MOAL handle + * @param data Source data + * @param size data length + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_process_init_cfg(moal_handle *handle, t_u8 *data, t_size size) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *pos; + t_u8 *intf_s, *intf_e; + t_u8 s[MAX_LINE_LEN]; /* 1 line data */ + t_size line_len; + t_u8 index = 0; + t_u32 i; + t_u8 bss_mac_addr[MAX_MAC_ADDR_LEN]; + t_u8 bss_mac_name[MAX_PARAM_LEN]; + t_u8 type[MAX_PARAM_LEN]; + t_u8 offset[MAX_PARAM_LEN]; + t_u8 value[MAX_PARAM_LEN]; + + ENTER(); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Needn't process this line */ + + /* Process MAC addr */ + if (strncmp(pos, "mac_addr", 8) == 0) { + intf_s = strchr(pos, '='); + if (intf_s != NULL) + intf_e = strchr(intf_s, ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + strncpy(bss_mac_addr, intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bss_mac_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(MERROR, + "Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(bss_mac_name, intf_s + 1, + intf_e - intf_s - 1); + bss_mac_name[intf_e - intf_s - 1] = '\0'; + for (i = 0; i < handle->priv_num; i++) { + if (strcmp(bss_mac_name, + handle->priv[i] + ->netdev->name) == + 0) { + memset(handle->priv[i] + ->current_addr, + 0, ETH_ALEN); + PRINTM(MINFO, + "Interface name: %s mac: %s\n", + bss_mac_name, + bss_mac_addr); + woal_mac2u8( + handle->priv[i] + ->current_addr, + bss_mac_addr); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (handle->priv[i]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) { + handle->priv[i] + ->current_addr[0] |= + 0x02; + PRINTM(MCMND, + "Set WFD device addr: " MACSTR + "\n", + MAC2STR(handle->priv[i] + ->current_addr)); + } +#endif +#endif +#endif + /* Set WLAN MAC addresses */ + if (MLAN_STATUS_SUCCESS != + woal_request_set_mac_address( + handle->priv[i], + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, + "Set MAC address failed\n"); + goto done; + } + moal_memcpy_ext( + handle, + handle->priv[i] + ->netdev + ->dev_addr, + handle->priv[i] + ->current_addr, + ETH_ALEN, ETH_ALEN); + index++; /* Mark found one + interface matching + */ + } + } + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp(pos, "wlan_reg", 8) == 0) { + intf_s = strchr(pos, '='); + if (intf_s != NULL) + intf_e = strchr(intf_s, ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + strncpy(type, intf_s + 1, 1); + type[1] = '\0'; + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = strchr(intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(MERROR, + "Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + strncpy(offset, intf_s, intf_e - intf_s); + offset[intf_e - intf_s] = '\0'; + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen(intf_s) >= MAX_PARAM_LEN)) { + PRINTM(MERROR, "Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + strncpy(value, intf_s, + MIN((MAX_PARAM_LEN - 1), strlen(intf_s))); + + if (MLAN_STATUS_SUCCESS != + woal_process_regrdwr(handle, type, offset, value)) { + PRINTM(MERROR, "Access Reg failed\n"); + goto done; + } + PRINTM(MINFO, "Reg type: %s, offset: %s, value: %s\n", + type, offset, value); + } + } + + if (index == 0) + PRINTM(MINFO, "Can't find any matching MAC Address"); + ret = MLAN_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief WOAL parse ASCII format raw data to hex format + * + * @param handle MOAL handle + * @param data Source data + * @param size data length + * @param wait_option wait option + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_process_hostcmd_cfg(moal_handle *handle, t_u8 *data, + t_size size, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = data; + t_u8 *intf_s, *intf_e; + t_u8 *buf = NULL; + t_u8 *ptr = NULL; + t_u32 cmd_len = 0; + t_u8 start_raw = MFALSE; + gfp_t flag; + +#define CMD_STR "MRVL_CMDhostcmd" +#define CMD_BUF_LEN 2048 + + ENTER(); + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(CMD_BUF_LEN, flag); + if (!buf) { + PRINTM(MERROR, "Could not allocate buffer space!\n"); + goto done; + } + ptr = buf; + strcpy(ptr, CMD_STR); + ptr = buf + strlen(CMD_STR) + sizeof(t_u32); + while ((pos - data) < size) { + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '#') { /* Line comment */ + while (*pos != '\n') + pos++; + pos++; + } + if ((*pos == '\r' && *(pos + 1) == '\n') || *pos == '\n' || + *pos == '\0') { + pos++; + continue; /* Needn't process this line */ + } + + if (*pos == '}') { + cmd_len = *((t_u16 *)(buf + strlen(CMD_STR) + + sizeof(t_u32) + sizeof(t_u16))); + moal_memcpy_ext(handle, buf + strlen(CMD_STR), &cmd_len, + sizeof(t_u32), + CMD_BUF_LEN - strlen(CMD_STR)); + + /* fire the hostcommand from here */ + woal_priv_hostcmd(handle->priv[0], buf, CMD_BUF_LEN, + wait_option); + memset(buf + strlen(CMD_STR), 0, + CMD_BUF_LEN - strlen(CMD_STR)); + ptr = buf + strlen(CMD_STR) + sizeof(t_u32); + start_raw = MFALSE; + pos++; + continue; + } + + if (start_raw == MFALSE) { + intf_s = strchr(pos, '='); + if (intf_s) + intf_e = strchr(intf_s, '{'); + else + intf_e = NULL; + + if (intf_s && intf_e) { + start_raw = MTRUE; + pos = intf_e + 1; + continue; + } + } + + if (start_raw) { + /* Raw data block exists */ + while (*pos != '\n') { + if ((*pos <= 'f' && *pos >= 'a') || + (*pos <= 'F' && *pos >= 'A') || + (*pos <= '9' && *pos >= '0')) { + *ptr++ = woal_atox(pos); + pos += 2; + } else + pos++; + } + } + } + +done: + kfree(buf); + LEAVE(); + return ret; +} +#define INIT_CFG_DATA 0x00 +#define INIT_HOSTCMD_CFG_DATA 0x02 +#define COUNTRY_POWER_TABLE 0x04 +#define BAND_STEER_CFG_DATA 0x08 + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +void woal_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware) + handle->user_data = firmware; + else + PRINTM(MERROR, "User init config request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_init_dpd_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->dpd_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_vdll_fw_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->firmware = firmware; + else + PRINTM(MERROR, "VDLL: Request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request firmware image for VDLL + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_vdll_req_fw(moal_handle *handle) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *vdll_fw = handle->drv_mode.fw_name; + + ENTER(); + if (vdll_fw) { + PRINTM(MMSG, "VDLL: Request firmware: %s\n", vdll_fw); + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, vdll_fw, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_vdll_fw_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, vdll_fw, + handle->hotplug_device, handle, + woal_request_vdll_fw_callback)) < 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, vdll_fw, + handle->hotplug_device, handle, + woal_request_vdll_fw_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "VDLL: request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->firmware, vdll_fw, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "VDLL: request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_init_txpwr_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->txpwr_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_init_cfg_data_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->init_cfg_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief WOAL set user defined init data and param + * + * @param handle MOAL handle structure + * @param type type argument + * @param wait_option wait option + * @param country_txpwrlimit Configure Tx Power Limit + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_set_user_init_data(moal_handle *handle, int type, + t_u8 wait_option, char *country_txpwrlimit) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *cfg_data = NULL; + t_size len; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *init_cfg = handle->params.init_cfg; + char *init_hostcmd_cfg = handle->params.init_hostcmd_cfg; + char *band_steer_cfg = handle->params.band_steer_cfg; + + ENTER(); + + if (type == INIT_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_cfg_data_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg, + handle->hotplug_device, handle, + woal_request_init_cfg_data_callback)) < 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, init_cfg, + handle->hotplug_device, handle, + woal_request_init_cfg_data_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "Init config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->init_cfg_data, init_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Init config file request_firmware() failed\n"); + goto done; + } + } + } else if (type == COUNTRY_POWER_TABLE) { + if (country_txpwrlimit == NULL) { + PRINTM(MERROR, + "The parameter 'country_txpwrlimit' is NULL\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* 'country_txpwrlimit' holds the value of Configured Tx Power + * Limit */ + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + country_txpwrlimit, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + country_txpwrlimit, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, country_txpwrlimit, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "country txpwrlimit config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + int status = request_firmware(&handle->user_data, + country_txpwrlimit, + handle->hotplug_device); + /* File does not exist, skip download */ + if (status == -ENOENT) { + ret = MLAN_STATUS_FILE_ERR; + PRINTM(MIOCTL, + "Country power table file does not exist\n"); + goto done; + } else if (status) { + PRINTM(MERROR, + "country txpwrlimit config file request_firmware() failed\n"); + goto done; + } + } + } else if (type == INIT_HOSTCMD_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + init_hostcmd_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + init_hostcmd_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, init_hostcmd_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "Init hostcmd config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->user_data, + init_hostcmd_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Init hostcmd config file request_firmware() failed\n"); + goto done; + } + } + } + if (type == BAND_STEER_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + band_steer_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + band_steer_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, band_steer_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "band_steer_cfg request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->user_data, + band_steer_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "band_steer_cfg file request_firmware() failed\n"); + goto done; + } + } + } + if (handle->user_data) { + cfg_data = (t_u8 *)(handle->user_data)->data; + len = (handle->user_data)->size; + if (type == INIT_HOSTCMD_CFG_DATA || + type == BAND_STEER_CFG_DATA || + type == COUNTRY_POWER_TABLE) { + if (MLAN_STATUS_SUCCESS != + woal_process_hostcmd_cfg(handle, cfg_data, len, + wait_option)) { + PRINTM(MERROR, + "Can't process hostcmd config file\n"); + goto done; + } + } + ret = MLAN_STATUS_SUCCESS; + } else if (type == INIT_CFG_DATA && handle->init_cfg_data) { + PRINTM(MIOCTL, "Load init_cfg success\n"); + ret = MLAN_STATUS_SUCCESS; + } +done: + if (handle->user_data) { + release_firmware(handle->user_data); + handle->user_data = NULL; + } + + LEAVE(); + return ret; +} + +/** + * @brief Configure aggrctrl + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_init_aggr_ctrl(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + pcfg_misc->sub_command = MLAN_OID_MISC_AGGR_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + req->action = MLAN_ACT_SET; + pcfg_misc->param.aggr_params.tx.enable = MTRUE; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Add interfaces DPC + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_add_card_dpc(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int i; + char str_buf[MLAN_MAX_VER_STR_LEN]; + ENTER(); + +#if defined(USB) + if (IS_USB(handle->card_type) && handle->boot_state == USB_FW_DNLD) { + /* Return now */ + LEAVE(); + return ret; + } +#endif /* USB_NEW_FW_DNLD */ + +#ifdef CONFIG_PROC_FS + /* Initialize proc fs */ + woal_proc_init(handle); +#endif /* CONFIG_PROC_FS */ + + /* Add interfaces */ + for (i = 0; i < handle->drv_mode.intf_num; i++) { + if (handle->drv_mode.bss_attr[i].bss_virtual) + continue; + if (!woal_add_interface(handle, handle->priv_num, + handle->drv_mode.bss_attr[i].bss_type)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + woal_get_version(handle, str_buf, sizeof(str_buf) - 1); + PRINTM(MMSG, "wlan: version = %s\n", str_buf); + +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode == MLAN_INIT_PARA_ENABLED) + goto done; +#endif + + if (handle->params.init_cfg && handle->init_cfg_data) { + if (MLAN_STATUS_SUCCESS != + woal_process_init_cfg(handle, + (t_u8 *)(handle->init_cfg_data)->data, + (handle->init_cfg_data)->size)) { + PRINTM(MERROR, "Can't process init config file\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + + if (moal_extflg_isset(handle, EXT_AGGR_CTRL)) { + /* Enable aggregation in FW */ + if (woal_init_aggr_ctrl(handle, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + +#ifdef USB + if (handle->params.usb_aggr == 1) { + /* Enable USB aggregation in FW */ + if (woal_usb_aggr_init(handle)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } +#endif + /* Add low power mode check */ + if (moal_extflg_isset(handle, EXT_LOW_PW_MODE) && + handle->card_info->low_power_enable && + woal_set_low_pwr_mode(handle, MOAL_IOCTL_WAIT)) { + /* Proceed with Warning */ + PRINTM(MERROR, "Unable to set Low Power Mode\n"); + } + +#if defined(SDIO) + if (IS_SD(handle->card_type)) + woal_set_sdio_slew_rate(handle); +#endif + + if (moal_extflg_isset(handle, EXT_PMIC) && handle->card_info->pmic) { + if (MLAN_STATUS_SUCCESS != + woal_pmic_configure(handle, MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, "Failed to configure PMIC\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + + if (handle->params.antcfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_antcfg(handle, MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, "Set user antcfg data failed\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + +#ifdef UAP_SUPPORT + if (handle->params.uap_oper_ctrl) + woal_set_uap_operation_ctrl(handle); +#endif + +#ifdef MFG_CMD_SUPPORT +done: +#endif +err: + if (handle->params.init_cfg && handle->init_cfg_data) { + release_firmware(handle->init_cfg_data); + handle->init_cfg_data = NULL; + } + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to add interface\n"); + + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + handle->priv_num = 0; +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + } + LEAVE(); + return ret; +} + +/** + * @brief Request dpd data + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_dpd_data(moal_handle *handle, + mlan_init_param *param) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *dpd_data_cfg = handle->params.dpd_data_cfg; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (dpd_data_cfg && strncmp(dpd_data_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + dpd_data_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_dpd_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + dpd_data_cfg, handle->hotplug_device, + handle, + woal_request_init_dpd_conf_callback)) < 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, dpd_data_cfg, + handle->hotplug_device, handle, + woal_request_init_dpd_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "DPD data request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + status = request_firmware(&handle->dpd_data, + dpd_data_cfg, + handle->hotplug_device); + if (status < 0 && status != -ENOENT) { + PRINTM(MERROR, + "DPD data request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (handle->dpd_data) { + param->pdpd_data_buf = (t_u8 *)handle->dpd_data->data; + param->dpd_data_len = handle->dpd_data->size; + } else { + param->dpd_data_len = UNKNOW_DPD_LENGTH; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Request TX Power data + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_txpwr_data(moal_handle *handle, + mlan_init_param *param) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *txpwrlimit_cfg = handle->params.txpwrlimit_cfg; + + ENTER(); + + if (txpwrlimit_cfg && strncmp(txpwrlimit_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + txpwrlimit_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_txpwr_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + txpwrlimit_cfg, handle->hotplug_device, + handle, + woal_request_init_txpwr_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, txpwrlimit_cfg, + handle->hotplug_device, handle, + woal_request_init_txpwr_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "Region txpwrlimit cfg data " + "request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->txpwr_data, + txpwrlimit_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, "Region txpwrlimit cfg data " + "request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (handle->txpwr_data) { + param->ptxpwr_data_buf = + (t_u8 *)handle->txpwr_data->data; + param->txpwr_data_len = handle->txpwr_data->size; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Request Cal data + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_cal_data(moal_handle *handle, + mlan_init_param *param) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *cal_data_cfg = handle->params.cal_data_cfg; + + ENTER(); + /** Cal data request */ + if (cal_data_cfg && strncmp(cal_data_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + cal_data_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + cal_data_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, cal_data_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "Cal data request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->user_data, cal_data_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Cal data request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } else if (!cal_data_cfg && handle->card_info->cal_data_cfg) { + PRINTM(MERROR, + "Please add cal_data_cfg for 8887/8977/8997/8987/8978\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (handle->user_data) { + param->pcal_data_buf = (t_u8 *)handle->user_data->data; + param->cal_data_len = handle->user_data->size; + } +done: + return ret; +} + +#if defined(USB) +/** + * @brief Download and Initialize firmware DPC + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_reset_usb_dev(moal_handle *handle) +{ + int ret = MLAN_STATUS_SUCCESS; + struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev; + int usbRstDev_ret = 0; + + ENTER(); + + /* Return now */ + PRINTM(MMSG, "WLAN FW is downloaded\n"); + + /* Reset USB device to get re-enumeration */ + if (udev) { +#define USB_WAIT_FW_READY (500) + mdelay(USB_WAIT_FW_READY); + usbRstDev_ret = usb_reset_device(udev); + if ((usbRstDev_ret == 0) || + (usbRstDev_ret == -ENODEV) || /* expected + since + chip + re-enumerates + */ + (usbRstDev_ret == -EINVAL)) { /* expected if USB FW detaches + first */ + PRINTM(MMSG, "usb_reset_device() successful.\n"); + } else { + PRINTM(MERROR, + "usb_reset_device() failed with error code =%d\n", + usbRstDev_ret); + ret = MLAN_STATUS_FAILURE; + } + } else { + PRINTM(MERROR, "ERR: No handle to call usb_reset_device()!\n"); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Download and Initialize firmware DPC + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_init_fw_dpc(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_fw_image fw; + mlan_init_param param; + + ENTER(); + + if (handle->firmware) { + memset(&fw, 0, sizeof(mlan_fw_image)); + fw.pfw_buf = (t_u8 *)handle->firmware->data; + fw.fw_len = handle->firmware->size; + if (handle->params.fw_reload == FW_RELOAD_SDIO_INBAND_RESET) + fw.fw_reload = handle->params.fw_reload; + else + fw.fw_reload = 0; + wifi_status = 0; + ret = mlan_dnld_fw(handle->pmlan_adapter, &fw); + if (ret == MLAN_STATUS_FAILURE) { + wifi_status = 1; + PRINTM(MERROR, + "WLAN: Fail download FW with nowwait: %u\n", + moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT)); + if (handle->ops.reg_dbg) + handle->ops.reg_dbg(handle); + goto done; + } + wifi_status = 0; + +#if defined(USB) + if (handle->boot_state == USB_FW_DNLD) { + if (!IS_USB8997(handle->card_type) && + !IS_USB9098(handle->card_type) && + !IS_USB9097(handle->card_type) && + !IS_USB8978(handle->card_type)) + ret = woal_reset_usb_dev(handle); + goto done; + } +#endif /* USB_NEW_FW_DNLD */ + PRINTM(MMSG, "WLAN FW is active\n"); + handle->driver_status = MFALSE; + } + + moal_get_boot_ktime(handle, &handle->on_time); + PRINTM(MMSG, "on_time is %llu\n", handle->on_time); + + /** data request */ + memset(¶m, 0, sizeof(mlan_init_param)); + + ret = woal_req_dpd_data(handle, ¶m); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + ret = woal_req_txpwr_data(handle, ¶m); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + ret = woal_req_cal_data(handle, ¶m); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + handle->hardware_status = HardwareStatusFwReady; +#ifdef USB + if (IS_USB(handle->card_type) && !handle->fw_reload) { + if (handle->skip_fw_dnld != MTRUE) { + ret = woal_usb_rx_init(handle); + if (ret == MLAN_STATUS_SUCCESS) + ret = woal_usb_tx_init(handle); + } + if (ret != MLAN_STATUS_SUCCESS) + goto done; + } +#endif /* USB */ + if (handle->fw_reload) + goto done; + handle->init_wait_q_woken = MFALSE; + + ret = mlan_set_init_param(handle->pmlan_adapter, ¶m); + ret = mlan_init_fw(handle->pmlan_adapter); + if (ret == MLAN_STATUS_FAILURE) { + wifi_status = 2; + goto done; + } else if (ret == MLAN_STATUS_SUCCESS) { + handle->hardware_status = HardwareStatusReady; + goto done; + } + /* Wait for mlan_init to complete */ + wait_event_timeout(handle->init_wait_q, handle->init_wait_q_woken, + 5 * HZ); + if (handle->hardware_status != HardwareStatusReady) { + woal_moal_debug_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + handle, MTRUE); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = MLAN_STATUS_SUCCESS; +done: + if (handle->dpd_data) { + release_firmware(handle->dpd_data); + handle->dpd_data = NULL; + } + if (handle->txpwr_data) { + release_firmware(handle->txpwr_data); + handle->txpwr_data = NULL; + } + if (handle->user_data) { + release_firmware(handle->user_data); + handle->user_data = NULL; + } + + LEAVE(); + return ret; +} + +/** + * @brief Request firmware DPC + * + * @param handle A pointer to moal_handle structure + * @param firmware A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_request_fw_dpc(moal_handle *handle, + const struct firmware *firmware) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + wifi_timeval tstamp; + ENTER(); + + if (!firmware) { + woal_get_monotonic_time(&tstamp); + if (tstamp.time_sec > + (handle->req_fw_time.time_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(MERROR, + "No firmware image found. Skipping download\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MERROR, + "request_firmware_nowait failed for %s. Retrying..\n", + handle->drv_mode.fw_name); + woal_sched_timeout(MOAL_TIMER_1S); + woal_request_fw(handle); + LEAVE(); + return ret; + } + handle->firmware = firmware; + + ret = woal_init_fw_dpc(handle); + if (ret) + goto done; + ret = woal_add_card_dpc(handle); + if (ret) + goto done; + +done: + /* We should hold the semaphore until callback finishes execution */ + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + LEAVE(); + return ret; +} + +/** + * @brief Request firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_fw_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + handle->firmware = firmware; + woal_request_fw_dpc((moal_handle *)context, firmware); + if (firmware) { + release_firmware(firmware); + handle->firmware = NULL; + } + LEAVE(); + return; +} + +/** + * @brief Download firmware using helper + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_request_fw(moal_handle *handle) +{ + int err; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + + ENTER(); + + PRINTM(MMSG, "Request firmware: %s\n", handle->drv_mode.fw_name); + + if (req_fw_nowait && !handle->fw_reload) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback); +#else + err = request_firmware_nowait(THIS_MODULE, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback); +#endif +#endif + if (err < 0) { + PRINTM(MFATAL, + "WLAN: request_firmware_nowait() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } + } else { + err = request_firmware(&handle->firmware, + handle->drv_mode.fw_name, + handle->hotplug_device); + if (err < 0) { + PRINTM(MFATAL, + "WLAN: request_firmware() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } else { + if (handle->fw_reload) + ret = woal_init_fw_dpc(handle); + else + ret = woal_request_fw_dpc(handle, + handle->firmware); + release_firmware(handle->firmware); + handle->firmware = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function initializes firmware + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_init_fw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + +#ifdef USB + if (IS_USB(handle->card_type)) { + if (MFALSE || (handle->skip_fw_dnld == MTRUE) +#if defined(USB) + || (handle->boot_state == USB_FW_READY) +#endif /* USB_NEW_FW_DNLD */ + ) { + PRINTM(MMSG, "WLAN FW is active\n"); + /* Set it to NULL to skip downloading firmware to card + */ + handle->firmware = NULL; + ret = woal_init_fw_dpc(handle); + if (ret) + goto done; + ret = woal_add_card_dpc(handle); + if (ret) + goto done; + /* Release semaphore if download is not required */ + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + done: + LEAVE(); + return ret; + } + } +#endif /* USB */ + + woal_get_monotonic_time(&handle->req_fw_time); + ret = woal_request_fw(handle); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MFATAL, "woal_request_fw failed\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function will fill in the mlan_buffer + * + * @param pmbuf A pointer to mlan_buffer + * @param skb A pointer to struct sk_buff + * + * @return N/A + */ +void woal_fill_mlan_buffer(moal_private *priv, mlan_buffer *pmbuf, + struct sk_buff *skb) +{ + wifi_timeval tstamp; + struct ethhdr *eth; + t_u8 tid = 0; + dot11_txcontrol *txcontrol; + t_u8 tx_ctrl_flag = MFALSE; + int i = 0; + ENTER(); + /* + * skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (IS_SKB_MAGIC_VLAN(skb)) { + tid = GET_VLAN_PRIO(skb); + } else { + eth = (struct ethhdr *)skb->data; + + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET]; + if (tid == 0xFF) + tid = (IPTOS_PREC(SKB_TOS(skb)) >> + IPTOS_OFFSET); + PRINTM(MDAT_D, + "packet type ETH_P_IP: dscp[%x], map[%x], tid=%d\n", + SKB_TOS(skb) >> DSCP_OFFSET, + priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET], + tid); + break; + case __constant_htons(ETH_P_IPV6): + tid = SKB_TIDV6(skb); + PRINTM(MDAT_D, + "packet type ETH_P_IPV6: %04x, tid=%#x prio=%#x\n", + eth->h_proto, tid, skb->priority); + break; + case __constant_htons(ETH_P_ARP): + tid = 0; + PRINTM(MDATA, "ARP packet %04x\n", eth->h_proto); + break; + default: + tid = 0; + if (priv->tx_protocols.protocol_num) { + for (i = 0; i < priv->tx_protocols.protocol_num; + i++) { + if (eth->h_proto == + __constant_htons( + priv->tx_protocols + .protocols[i])) + tx_ctrl_flag = MTRUE; + } + } + if (tx_ctrl_flag) { + txcontrol = (dot11_txcontrol + *)(skb->data + + sizeof(struct ethhdr)); + pmbuf->u.tx_info.data_rate = + txcontrol->datarate; + pmbuf->u.tx_info.channel = txcontrol->channel; + pmbuf->u.tx_info.bw = txcontrol->bw; + pmbuf->u.tx_info.tx_power.val = + txcontrol->power; + pmbuf->u.tx_info.retry_limit = + txcontrol->retry_limit; + tid = txcontrol->priority; + memmove(skb->data + sizeof(dot11_txcontrol), + skb->data, sizeof(struct ethhdr)); + skb_pull(skb, sizeof(dot11_txcontrol)); + pmbuf->flags |= MLAN_BUF_FLAG_TX_CTRL; + } + break; + } + } + + skb->priority = tid; + + /* Record the current time the packet was queued; used to determine + * the amount of time the packet was queued in the driver before it + * was sent to the firmware. The delay is then sent along with the + * packet to the firmware for aggregate delay calculation for stats + * and MSDU lifetime expiry. + */ + woal_get_monotonic_time(&tstamp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + skb->tstamp = ktime_get_raw(); +#else + skb->tstamp = timeval_to_ktime(tstamp); +#endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + skb_set_timestamp(skb, &tstamp); +#else + moal_memcpy_ext(priv->phandle, &skb->stamp, &tstamp, sizeof(skb->stamp), + sizeof(skb->stamp)); +#endif + + pmbuf->pdesc = skb; + pmbuf->pbuf = skb->head + sizeof(mlan_buffer); +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + pmbuf->buf_pa = 0; + } +#endif + pmbuf->data_offset = skb->data - (skb->head + sizeof(mlan_buffer)); + pmbuf->data_len = skb->len; + pmbuf->priority = skb->priority; + pmbuf->buf_type = 0; + pmbuf->in_ts_sec = tstamp.time_sec; + pmbuf->in_ts_usec = tstamp.time_usec; + + LEAVE(); + return; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) +static struct device_type wlan_type = { + .name = "wlan", +}; +#endif + +#ifdef STA_SUPPORT +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** Network device handlers */ +const struct net_device_ops woal_netdev_ops = { + .ndo_open = woal_open, + .ndo_start_xmit = woal_hard_start_xmit, + .ndo_stop = woal_close, + .ndo_do_ioctl = woal_do_ioctl, + .ndo_set_mac_address = woal_set_mac_address, + .ndo_tx_timeout = woal_tx_timeout, + .ndo_get_stats = woal_get_stats, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .ndo_set_rx_mode = woal_set_multicast_list, +#else + .ndo_set_multicast_list = woal_set_multicast_list, +#endif + .ndo_select_queue = woal_select_queue, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + +/** + * @brief This function initializes the private structure + * and dev structure for station mode + * + * @param dev A pointer to net_device structure + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status woal_init_sta_dev(struct net_device *dev, moal_private *priv) +{ + ENTER(); + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->open = woal_open; + dev->hard_start_xmit = woal_hard_start_xmit; + dev->stop = woal_close; + dev->do_ioctl = woal_do_ioctl; + dev->set_mac_address = woal_set_mac_address; + dev->tx_timeout = woal_tx_timeout; + dev->get_stats = woal_get_stats; + dev->set_multicast_list = woal_set_multicast_list; +#else + dev->netdev_ops = &woal_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#else + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#endif +#ifdef STA_WEXT + if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *)&woal_handler_def; + } +#endif + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) + init_waitqueue_head(&priv->ft_wait_q); +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** Network device handlers */ +const struct net_device_ops woal_uap_netdev_ops = { + .ndo_open = woal_open, + .ndo_start_xmit = woal_hard_start_xmit, + .ndo_stop = woal_close, + .ndo_do_ioctl = woal_uap_do_ioctl, + .ndo_set_mac_address = woal_set_mac_address, + .ndo_tx_timeout = woal_tx_timeout, + .ndo_get_stats = woal_get_stats, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .ndo_set_rx_mode = woal_uap_set_multicast_list, +#else + .ndo_set_multicast_list = woal_uap_set_multicast_list, +#endif + .ndo_select_queue = woal_select_queue, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + +/** + * @brief This function initializes the private structure + * and dev structure for uap mode + * + * @param dev A pointer to net_device structure + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status woal_init_uap_dev(struct net_device *dev, moal_private *priv) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->open = woal_open; + dev->hard_start_xmit = woal_hard_start_xmit; + dev->stop = woal_close; + dev->set_mac_address = woal_set_mac_address; + dev->tx_timeout = woal_tx_timeout; + dev->get_stats = woal_get_stats; + dev->do_ioctl = woal_uap_do_ioctl; + dev->set_multicast_list = woal_uap_set_multicast_list; +#else + dev->netdev_ops = &woal_uap_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_UAP_WATCHDOG_TIMEOUT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#else + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#endif +#ifdef UAP_WEXT + if (IS_UAP_WEXT(priv->phandle->params.cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_uap_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *)&woal_uap_handler_def; + } +#endif /* UAP_WEXT */ + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + LEAVE(); + return status; +} +#endif /* UAP_SUPPORT */ + +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private *woal_add_interface(moal_handle *handle, t_u8 bss_index, + t_u8 bss_type) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + char name[256]; + int i = 0; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + char csa_str[256]; +#endif +#endif + ENTER(); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#define MAX_WMM_QUEUE 4 + /* Allocate an Ethernet device */ + dev = alloc_etherdev_mq(sizeof(moal_private), MAX_WMM_QUEUE); +#else + dev = alloc_etherdev(sizeof(moal_private)); +#endif + if (!dev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + goto error; + } + /* Allocate device name */ +#ifdef STA_SUPPORT + memset(name, 0, sizeof(name)); + if (handle->params.sta_name) + snprintf(name, sizeof(name), "%s%%d", handle->params.sta_name); + else if (handle->second_mac) + snprintf(name, sizeof(name), "m%s", default_mlan_name); + else + snprintf(name, sizeof(name), "%s", default_mlan_name); + + if ((bss_type == MLAN_BSS_TYPE_STA) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate mlan device name\n"); + goto error; + } +#endif +#ifdef UAP_SUPPORT + memset(name, 0, sizeof(name)); + if (handle->params.uap_name) + snprintf(name, sizeof(name), "%s%%d", handle->params.uap_name); + else if (handle->second_mac) + snprintf(name, sizeof(name), "m%s", default_uap_name); + else + snprintf(name, sizeof(name), "%s", default_uap_name); + if ((bss_type == MLAN_BSS_TYPE_UAP) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate uap device name\n"); + goto error; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + memset(name, 0, sizeof(name)); + if (handle->params.wfd_name) + snprintf(name, sizeof(name), "%s%%d", handle->params.wfd_name); + else if (handle->second_mac) + snprintf(name, sizeof(name), "m%s", default_wfd_name); + else + snprintf(name, sizeof(name), "%s", default_wfd_name); + if ((bss_type == MLAN_BSS_TYPE_WIFIDIRECT) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate wifidirect device name\n"); + goto error; + } +#endif + priv = (moal_private *)netdev_priv(dev); + /* Save the priv to handle */ + handle->priv[bss_index] = priv; + + /* Use the same handle structure */ + priv->phandle = handle; + priv->netdev = dev; + priv->bss_index = bss_index; + priv->bss_type = bss_type; + priv->extra_tx_head_len = 0; + if (bss_type == MLAN_BSS_TYPE_STA) + priv->bss_role = MLAN_BSS_ROLE_STA; + else if (bss_type == MLAN_BSS_TYPE_UAP) + priv->bss_role = MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->bss_role = MLAN_BSS_ROLE_STA; +#endif + + INIT_LIST_HEAD(&priv->tcp_sess_queue); + spin_lock_init(&priv->tcp_sess_lock); + + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_lock_init(&priv->tx_stat_lock); + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + spin_lock_init(&priv->connect_lock); +#endif +#endif + +#ifdef STA_SUPPORT + INIT_LIST_HEAD(&priv->pmksa_cache_list); + if (bss_type == MLAN_BSS_TYPE_STA) { + init_waitqueue_head(&priv->okc_wait_q); + spin_lock_init(&priv->pmksa_list_lock); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } +#endif +#if defined(DRV_EMBEDDED_AUTHENTICATOR) + init_waitqueue_head(&priv->hostcmd_wait_q); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + SET_MODULE_OWNER(dev); +#endif +#ifdef STA_SUPPORT + if (bss_type == MLAN_BSS_TYPE_STA +#ifdef WIFI_DIRECT_SUPPORT + || bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) + woal_init_sta_dev(dev, priv); +#endif +#ifdef UAP_SUPPORT + if (bss_type == MLAN_BSS_TYPE_UAP) { + if (MLAN_STATUS_SUCCESS != woal_init_uap_dev(dev, priv)) + goto error; + } +#endif + if (!handle->priv_num +#ifdef MFG_CMD_SUPPORT + && (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + ) { + if (handle->params.init_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, INIT_CFG_DATA, + MOAL_IOCTL_WAIT, NULL)) { + PRINTM(MFATAL, + "Set user init data and param failed\n"); + goto error; + } + } + if (handle->params.init_hostcmd_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, + INIT_HOSTCMD_CFG_DATA, + MOAL_IOCTL_WAIT, NULL)) { + PRINTM(MFATAL, + "Set user init hostcmd data and param failed\n"); + goto error; + } + } + } +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) { +#endif + if (bss_type == MLAN_BSS_TYPE_UAP && + priv->phandle->params.band_steer_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(priv->phandle, + BAND_STEER_CFG_DATA, + MOAL_IOCTL_WAIT, NULL)) { + PRINTM(MFATAL, + "Set band steering configure param failed\n"); + } + } +#ifdef MFG_CMD_SUPPORT + } +#endif + + handle->priv_num++; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (!priv->phandle->wiphy && + IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) { + if (woal_register_cfg80211(priv)) { + PRINTM(MERROR, "Cannot register with cfg80211\n"); + goto error; + } + } +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if ((priv->bss_role == MLAN_BSS_ROLE_STA) && + IS_STA_CFG80211(handle->params.cfg80211_wext)) { + if (bss_type == MLAN_BSS_TYPE_STA +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + || bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif +#endif + ) + /* Register cfg80211 for STA or Wifi direct */ + if (woal_register_sta_cfg80211(dev, bss_type)) { + PRINTM(MERROR, + "Cannot register STA with cfg80211\n"); + goto error; + } + } +#endif /* STA_SUPPORT */ +#endif /* STA_CFG80211 */ +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if ((priv->bss_role == MLAN_BSS_ROLE_UAP) && + IS_UAP_CFG80211(handle->params.cfg80211_wext)) { + /* Register cfg80211 for UAP */ + if (woal_register_uap_cfg80211(dev, bss_type)) { + PRINTM(MERROR, "Cannot register UAP with cfg80211\n"); + goto error; + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + strcpy(csa_str, "CSA"); + strcat(csa_str, name); + priv->csa_workqueue = alloc_workqueue( + csa_str, WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); + if (!priv->csa_workqueue) { + PRINTM(MERROR, "cannot alloc csa workqueue \n"); + goto error; + } + INIT_DELAYED_WORK(&priv->csa_work, woal_csa_work_queue); +#endif +#endif +#endif /*UAP_CFG80211 */ + + /* Initialize priv structure */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + SET_NETDEV_DEV(dev, handle->hotplug_device); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) + SET_NETDEV_DEVTYPE(dev, &wlan_type); +#endif + + /* Register network device */ + if (register_netdev(dev)) { + PRINTM(MERROR, "Cannot register virtual network device\n"); + goto error; + } + netif_carrier_off(dev); + woal_stop_queue(dev); + + PRINTM(MINFO, "%s: NXP 802.11 Adapter\n", dev->name); + + if (bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { +#if defined(SD8887) || defined(SD8987) + mlan_fw_info fw_info; + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (MFALSE +#ifdef SD8887 + || IS_SD8887(handle->card_type) +#endif +#ifdef SD8987 + || IS_SD8987(handle->card_type) +#endif + ) { + if ((fw_info.antinfo & ANT_DIVERSITY_2G) && + (fw_info.antinfo & ANT_DIVERSITY_5G)) + handle->card_info->histogram_table_num = 4; + } +#endif + + for (i = 0; i < handle->card_info->histogram_table_num; i++) { + priv->hist_data[i] = + kmalloc(sizeof(hgm_data) + + handle->card_info->rx_rate_max * + sizeof(atomic_t), + GFP_KERNEL); + if (!(priv->hist_data[i])) { + PRINTM(MERROR, + "kmalloc priv->hist_data[%d] failed\n", + i); + goto error; + } + } + woal_hist_data_reset(priv); + } +#ifdef CONFIG_PROC_FS + woal_create_proc_entry(priv); + woal_debug_entry(priv); +#endif /* CONFIG_PROC_FS */ + + LEAVE(); + return priv; +error: + handle->priv_num = bss_index; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (priv) { + if (priv->wdev && + IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) + priv->wdev = NULL; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_workqueue) { + destroy_workqueue(priv->csa_workqueue); + priv->csa_workqueue = NULL; + } +#endif +#endif + } +#endif + if (dev && dev->reg_state == NETREG_REGISTERED) + unregister_netdev(dev); + if (dev) + free_netdev(dev); + LEAVE(); + return NULL; +} + +/** + * @brief This function removes an interface. + * + * @param handle A pointer to the moal_handle structure + * @param bss_index BSS index number + * + * @return N/A + */ +void woal_remove_interface(moal_handle *handle, t_u8 bss_index) +{ + struct net_device *dev = NULL; + moal_private *priv = handle->priv[bss_index]; +#if defined(STA_WEXT) || defined(UAP_WEXT) + union iwreq_data wrqu; +#endif + int i = 0; + ENTER(); + + if (!priv || !priv->netdev) + goto error; + dev = priv->netdev; + + if (priv->media_connected == MTRUE) { + priv->media_connected = MFALSE; +#if defined(STA_WEXT) || defined(UAP_WEXT) + if (IS_STA_OR_UAP_WEXT(handle->params.cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, + NULL); + } +#endif + } + woal_flush_tcp_sess_queue(priv); + + woal_flush_tx_stat_queue(priv); + +#ifdef STA_CFG80211 + if (priv->bss_type == MLAN_BSS_TYPE_STA && + IS_STA_CFG80211(handle->params.cfg80211_wext)) { + woal_flush_pmksa_list(priv); + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } +#endif + + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + for (i = 0; i < handle->card_info->histogram_table_num; i++) { + kfree(priv->hist_data[i]); + priv->hist_data[i] = NULL; + } + } +#ifdef CONFIG_PROC_FS + /* Remove proc debug */ + woal_debug_remove(priv); + woal_proc_remove(priv); +#endif /* CONFIG_PROC_FS */ + /* Last reference is our one */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt)); +#else + PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev)); +#endif + + PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name); + + if (dev->reg_state == NETREG_REGISTERED) + unregister_netdev(dev); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (priv->wdev && IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) + priv->wdev = NULL; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_workqueue) { + flush_workqueue(priv->csa_workqueue); + destroy_workqueue(priv->csa_workqueue); + priv->csa_workqueue = NULL; + } +#endif +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + woal_deinit_wifi_hal(priv); +#endif + + /* Clear the priv in handle */ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 13) + if (IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) + priv->phandle->wiphy->extended_capabilities = NULL; +#endif +#endif + priv->phandle->priv[priv->bss_index] = NULL; + priv->phandle = NULL; + priv->netdev = NULL; + free_netdev(dev); +error: + LEAVE(); + return; +} + +/** + * @brief Configure pmic in firmware + * + * @param handle A pointer to moal_handle + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_pmic_configure(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_PMIC_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Configure antcfg + * + * @param handle A pointer to moal_handle + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_set_user_antcfg(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_status status; + int antcfg; + + ENTER(); + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + antcfg = handle->params.antcfg; + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + + if (handle->feature_control & FEATURE_CTRL_STREAM_2X2) { + if (IS_CARD9098(handle->card_type) || + IS_CARD9097(handle->card_type)) + radio->param.ant_cfg.tx_antenna = + radio->param.ant_cfg.rx_antenna = antcfg; + else { + radio->param.ant_cfg.tx_antenna = + (antcfg & 0x0030) >> 4; + radio->param.ant_cfg.rx_antenna = antcfg & 0x0003; + } + } else + radio->param.ant_cfg_1x1.antenna = antcfg; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Configure MLAN for low power mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_set_low_pwr_mode(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_LOW_PWR_MODE; + misc->param.low_pwr_mode = moal_extflg_isset(handle, EXT_LOW_PW_MODE); + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Send FW shutdown command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_shutdown_fw(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_INIT_SHUTDOWN; + misc->param.func_init_shutdown = MLAN_FUNC_SHUTDOWN; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + /* add 100 ms delay to avoid back to back init/shutdown */ + mdelay(100); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Return hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +int woal_hexval(char chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +#ifdef STA_SUPPORT +#endif + +/** + * @brief This function flush event queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_flush_evt_queue(moal_handle *handle) +{ + struct woal_event *evt = NULL, *tmp_node; + unsigned long flags; + spin_lock_irqsave(&handle->evt_lock, flags); + list_for_each_entry_safe (evt, tmp_node, &handle->evt_queue, link) { + list_del(&evt->link); + spin_unlock_irqrestore(&handle->evt_lock, flags); + kfree(evt); + spin_lock_irqsave(&handle->evt_lock, flags); + } + INIT_LIST_HEAD(&handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); +} + +/** + * @brief This function cancel all works in the queue + * and destroy the main workqueue. + * + * @param handle A pointer to moal_handle + * + * @return N/A + */ +void woal_terminate_workqueue(moal_handle *handle) +{ + ENTER(); + + /* Terminate main workqueue */ + if (handle->workqueue) { + flush_workqueue(handle->workqueue); + destroy_workqueue(handle->workqueue); + handle->workqueue = NULL; + } + if (handle->rx_workqueue) { + flush_workqueue(handle->rx_workqueue); + destroy_workqueue(handle->rx_workqueue); + handle->rx_workqueue = NULL; + } + if (handle->evt_workqueue) { + woal_flush_evt_queue(handle); + flush_workqueue(handle->evt_workqueue); + destroy_workqueue(handle->evt_workqueue); + handle->evt_workqueue = NULL; + } + if (handle->tx_workqueue) { + flush_workqueue(handle->tx_workqueue); + destroy_workqueue(handle->tx_workqueue); + handle->tx_workqueue = NULL; + } + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function opens the network device + * + * @param dev A pointer to net_device structure + * + * @return 0 --success, otherwise fail + */ +int woal_open(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); +#if defined(USB) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + struct usb_interface *intf = + ((struct usb_card_rec *)priv->phandle->card)->intf; +#endif /* < 2.6.34 */ +#endif /* USB_SUSPEND_RESUME */ + t_u8 carrier_on = MFALSE; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "open is not allowed in surprise remove state.\n"); + LEAVE(); + return -EFAULT; + } +#if defined(USB) + if (IS_USB(priv->phandle->card_type)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + intf->pm_usage_cnt = 1; +#else + atomic_set(&intf->pm_usage_cnt, 1); +#endif /* < 2.6.32 */ + usb_autopm_put_interface(intf); +#else + struct usb_device *udev = + ((struct usb_card_rec *)(priv->phandle->card))->udev; + usb_lock_device(udev); + atomic_set(&udev->dev.power.usage_count, 1); + usb_enable_autosuspend(udev); + usb_unlock_device(udev); +#endif /* < 2.6.34 */ + } +#endif /* USB_SUSPEND_RESUME */ + +#if defined(USB) || defined(SYSKT) + /* On some systems the device open handler will be called before HW + ready. + Use the following flag check and wait function to work around + the issue. */ + if (MTRUE +#ifdef SDIO + && !IS_SD(priv->phandle->card_type) +#endif +#ifdef PCIE + && !IS_PCIE(priv->phandle->card_type) +#endif + ) { + int i = 0; + + while ((priv->phandle->hardware_status != + HardwareStatusReady) && + (i < MAX_WAIT_DEVICE_READY_COUNT)) { + i++; + woal_sched_timeout(100); + } + if (i >= MAX_WAIT_DEVICE_READY_COUNT) { + PRINTM(MFATAL, + "HW not ready, wlan_open() return failure\n"); + LEAVE(); + return -EFAULT; + } + } +#endif /* USB || SYSKT || SYSKT_MULTI */ + if (!MODULE_GET) { + LEAVE(); + return -EFAULT; + } +#ifdef UAP_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + (priv->media_connected)) + carrier_on = MTRUE; +#endif +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->media_connected || priv->is_adhoc_link_sensed)) + carrier_on = MTRUE; +#endif + if (carrier_on == MTRUE) { + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + } else { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + + LEAVE(); + return 0; +} + +/** + * @brief This function closes the network device + * + * @param dev A pointer to net_device structure + * + * @return 0 + */ +int woal_close(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); +#if defined(USB) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + struct usb_interface *intf = + ((struct usb_card_rec *)priv->phandle->card)->intf; +#endif /* < 2.6.34 */ +#endif /* USB_SUSPEND_RESUME */ +#ifdef STA_CFG80211 + int cfg80211_wext = priv->phandle->params.cfg80211_wext; +#endif + ENTER(); + + woal_flush_tx_stat_queue(priv); + +#ifdef STA_SUPPORT +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext) && + (priv->bss_type == MLAN_BSS_TYPE_STA)) + woal_clear_conn_params(priv); + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev->current_bss) { + priv->cfg_disconnect = MTRUE; + cfg80211_disconnected(priv->netdev, 0, NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + true, +#endif + GFP_KERNEL); + } +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext) && priv->sched_scanning) { + woal_stop_bg_scan(priv, MOAL_IOCTL_WAIT); + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + cfg80211_sched_scan_stopped(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + priv->bg_scan_reqid +#endif + ); + priv->sched_scanning = MFALSE; + } +#endif +#endif +#endif + if (!priv->bss_virtual) + woal_stop_queue(priv->netdev); + MODULE_PUT; +#if defined(USB) + if (IS_USB(priv->phandle->card_type)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + intf->pm_usage_cnt = 0; +#else + atomic_set(&intf->pm_usage_cnt, 0); +#endif /* < 2.6.32 */ + usb_autopm_get_interface(intf); +#else + struct usb_device *udev = + ((struct usb_card_rec *)(priv->phandle->card))->udev; + usb_lock_device(udev); + atomic_set(&udev->dev.power.usage_count, 0); + usb_disable_autosuspend(udev); + usb_unlock_device(udev); +#endif /* < 2.6.34 */ + } +#endif /* USB_SUSPEND_RESUME */ + + LEAVE(); + return 0; +} + +/** + * @brief This function sets the MAC address to firmware. + * + * @param dev A pointer to mlan_private structure + * @param addr MAC address to set + * + * @return 0 --success, otherwise fail + */ +int woal_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct sockaddr *phw_addr = (struct sockaddr *)addr; + t_u8 prev_addr[ETH_ALEN]; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "Set mac address is not allowed in surprise remove state.\n"); + LEAVE(); + return -EFAULT; + } + + moal_memcpy_ext(priv->phandle, prev_addr, priv->current_addr, ETH_ALEN, + ETH_ALEN); + memset(priv->current_addr, 0, ETH_ALEN); + /* dev->dev_addr is 6 bytes */ + HEXDUMP("dev->dev_addr:", dev->dev_addr, ETH_ALEN); + + HEXDUMP("addr:", (t_u8 *)phw_addr->sa_data, ETH_ALEN); + moal_memcpy_ext(priv->phandle, priv->current_addr, phw_addr->sa_data, + ETH_ALEN, ETH_ALEN); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->current_addr[0] |= 0x02; + PRINTM(MCMND, "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } +#endif +#endif +#endif + if (MLAN_STATUS_SUCCESS != + woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Set MAC address failed\n"); + /* For failure restore the MAC address */ + moal_memcpy_ext(priv->phandle, priv->current_addr, prev_addr, + ETH_ALEN, ETH_ALEN); + ret = -EFAULT; + goto done; + } + HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN); + moal_memcpy_ext(priv->phandle, dev->dev_addr, priv->current_addr, + ETH_ALEN, ETH_ALEN); +done: + LEAVE(); + return ret; +} + +/** + * @brief Check driver status + * + * @param handle A pointer to moal_handle + * + * @return MTRUE/MFALSE + */ +t_u8 woal_check_driver_status(moal_handle *handle) +{ + moal_private *priv = NULL; + wifi_timeval t; + int i = 0; + mlan_debug_info *info = &(handle->debug_info); + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv || woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return MTRUE; + } +#define MOAL_CMD_TIMEOUT_MAX 9 +#define MOAL_CMD_TIMEOUT 20 + woal_get_monotonic_time(&t); + if (info->dnld_cmd_in_secs && info->pending_cmd && + (t.time_sec > (info->dnld_cmd_in_secs + MOAL_CMD_TIMEOUT_MAX))) { + if (t.time_sec > (info->dnld_cmd_in_secs + MOAL_CMD_TIMEOUT) && + !info->num_cmd_timeout) { + PRINTM(MERROR, "Ignore invalid time, wait=%d\n", + (int)(t.time_sec - info->dnld_cmd_in_secs)); + } else { + PRINTM(MERROR, "Timeout cmd id = 0x%x wait=%d\n", + info->pending_cmd, + (int)(t.time_sec - info->dnld_cmd_in_secs)); + LEAVE(); + return MTRUE; + } + } + if (info->num_cmd_timeout) { + PRINTM(MERROR, "num_cmd_timeout = %d\n", info->num_cmd_timeout); + PRINTM(MERROR, "Timeout cmd id = 0x%x, act = 0x%x\n", + info->timeout_cmd_id, info->timeout_cmd_act); + LEAVE(); + return MTRUE; + } + if (info->num_cmd_host_to_card_failure) { + PRINTM(MERROR, "num_cmd_host_to_card_failure = %d\n", + info->num_cmd_host_to_card_failure); + LEAVE(); + return MTRUE; + } + if (info->num_no_cmd_node) { + PRINTM(MERROR, "num_no_cmd_node = %d\n", info->num_no_cmd_node); + LEAVE(); + return MTRUE; + } + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->num_tx_timeout >= NUM_TX_TIMEOUT_THRESHOLD) { + PRINTM(MERROR, "num_tx_timeout = %d\n", + priv->num_tx_timeout); + LEAVE(); + return MTRUE; + } + } + } + if (info->pm_wakeup_card_req && info->pm_wakeup_fw_try) { +#define MAX_WAIT_TIME 3 + if (t.time_sec > (info->pm_wakeup_in_secs + MAX_WAIT_TIME)) { + PRINTM(MERROR, + "wakeup_dev_req=%d wakeup_tries=%d wait=%d\n", + info->pm_wakeup_card_req, info->pm_wakeup_fw_try, + (int)(t.time_sec - info->pm_wakeup_in_secs)); + LEAVE(); + return MTRUE; + } + } + if (info->fw_hang_report) { + PRINTM(MERROR, "fw_hang_report = %d\n", info->fw_hang_report); + LEAVE(); + return MTRUE; + } + + if (priv && priv->phandle->driver_status) { + LEAVE(); + return MTRUE; + } + + LEAVE(); + return MFALSE; +} + +/** + * @brief Display MLAN debug information + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void woal_mlan_debug_info(moal_private *priv) +{ + int i; +#ifdef SDIO + int j; + t_u8 mp_aggr_pkt_limit = 0; +#endif + char str[512] = {0}; + char *s; + mlan_debug_info *info = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or priv->phandle is null\n"); + LEAVE(); + return; + } + + info = &(priv->phandle->debug_info); + + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return; + } + PRINTM(MERROR, "------------mlan_debug_info-------------\n"); + PRINTM(MERROR, "mlan_processing =%d\n", info->mlan_processing); + PRINTM(MERROR, "main_lock_flag =%d\n", info->main_lock_flag); + PRINTM(MERROR, "main_process_cnt =%d\n", info->main_process_cnt); + PRINTM(MERROR, "delay_task_flag =%d\n", info->delay_task_flag); + PRINTM(MERROR, "mlan_rx_processing =%d\n", info->mlan_rx_processing); + PRINTM(MERROR, "rx_pkts_queued=%d\n", info->rx_pkts_queued); + PRINTM(MERROR, "tx_pkts_queued=%d\n", info->tx_pkts_queued); + PRINTM(MERROR, "fw_hang_report = %d\n", info->fw_hang_report); + PRINTM(MERROR, "num_cmd_timeout = %d\n", info->num_cmd_timeout); + PRINTM(MERROR, "Timeout cmd id = 0x%x, act = 0x%x\n", + info->timeout_cmd_id, info->timeout_cmd_act); + + PRINTM(MERROR, "last_cmd_index = %d\n", info->last_cmd_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_id[i]); + PRINTM(MERROR, "last_cmd_id = %s\n", str); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_act[i]); + PRINTM(MERROR, "last_cmd_act = %s\n", str); + PRINTM(MERROR, "last_cmd_resp_index = %d\n", info->last_cmd_resp_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_resp_id[i]); + PRINTM(MERROR, "last_cmd_resp_id = %s\n", str); + PRINTM(MERROR, "last_event_index = %d\n", info->last_event_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_event[i]); + PRINTM(MERROR, "last_event = %s", str); + + PRINTM(MERROR, "num_data_h2c_failure = %d\n", + info->num_tx_host_to_card_failure); + PRINTM(MERROR, "num_cmd_h2c_failure = %d\n", + info->num_cmd_host_to_card_failure); + PRINTM(MERROR, "num_alloc_buffer_failure = %d\n", + info->num_alloc_buffer_failure); + PRINTM(MERROR, "num_pkt_dropped = %d\n", info->num_pkt_dropped); + +#ifdef SDIO + if (IS_SD(priv->phandle->card_type)) { + PRINTM(MERROR, "num_data_c2h_failure = %d\n", + info->num_rx_card_to_host_failure); + PRINTM(MERROR, "num_cmdevt_c2h_failure = %d\n", + info->num_cmdevt_card_to_host_failure); + PRINTM(MERROR, "num_int_read_failure = %d\n", + info->num_int_read_failure); + PRINTM(MERROR, "last_int_status = %d\n", info->last_int_status); + + PRINTM(MERROR, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + (unsigned int)info->mp_rd_bitmap, info->curr_rd_port); + PRINTM(MERROR, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + (unsigned int)info->mp_wr_bitmap, info->curr_wr_port); + PRINTM(MERROR, "mp_invalid_update=%d\n", + info->mp_invalid_update); + mp_aggr_pkt_limit = info->mp_aggr_pkt_limit; + PRINTM(MERROR, "last_recv_wr_bitmap=0x%x last_mp_index = %d\n", + info->last_recv_wr_bitmap, info->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + for (s = str, j = 0; j < mp_aggr_pkt_limit; j++) + s += sprintf( + s, "0x%02x ", + info->last_mp_wr_info + [i * mp_aggr_pkt_limit + j]); + + PRINTM(MERROR, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n%s\n", + info->last_mp_wr_bitmap[i], + info->last_mp_wr_ports[i], + info->last_mp_wr_len[i], + info->last_curr_wr_port[i], str); + } + } +#endif +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + PRINTM(MERROR, "txbd_rdptr=0x%x txbd_wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + PRINTM(MERROR, "rxbd_rdptr=0x%x rxbd_wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + PRINTM(MERROR, "eventbd_rdptr=0x%x event_wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + PRINTM(MERROR, "last_wr_index:%d\n", + info->txbd_wrptr & (MLAN_MAX_TXRX_BD - 1)); + PRINTM(MERROR, "Tx pkt size:\n"); + for (s = str, i = 0; i < MLAN_MAX_TXRX_BD; i++) { + s += sprintf(s, "%d ", info->last_tx_pkt_size[i]); + if (((i + 1) % 16) == 0) { + PRINTM(MERROR, "%s\n", str); + s = str; + } + } + } +#endif + PRINTM(MERROR, "num_event_deauth = %d\n", info->num_event_deauth); + PRINTM(MERROR, "num_event_disassoc = %d\n", info->num_event_disassoc); + PRINTM(MERROR, "num_event_link_lost = %d\n", info->num_event_link_lost); + PRINTM(MERROR, "num_cmd_deauth = %d\n", info->num_cmd_deauth); + PRINTM(MERROR, "num_cmd_assoc_success = %d\n", + info->num_cmd_assoc_success); + PRINTM(MERROR, "num_cmd_assoc_failure = %d\n", + info->num_cmd_assoc_failure); + PRINTM(MERROR, "num_cons_assoc_failure = %d\n", + info->num_cons_assoc_failure); + PRINTM(MERROR, "cmd_resp_received = %d\n", info->cmd_resp_received); + PRINTM(MERROR, "event_received = %d\n", info->event_received); + + PRINTM(MERROR, "max_tx_buf_size = %d\n", info->max_tx_buf_size); + PRINTM(MERROR, "tx_buf_size = %d\n", info->tx_buf_size); + PRINTM(MERROR, "curr_tx_buf_size = %d\n", info->curr_tx_buf_size); + + PRINTM(MERROR, "data_sent=%d cmd_sent=%d\n", info->data_sent, + info->cmd_sent); + + PRINTM(MERROR, "ps_mode=%d ps_state=%d\n", info->ps_mode, + info->ps_state); + PRINTM(MERROR, "wakeup_dev_req=%d wakeup_tries=%d wakeup_timeout=%d\n", + info->pm_wakeup_card_req, info->pm_wakeup_fw_try, + info->pm_wakeup_timeout); + PRINTM(MERROR, "hs_configured=%d hs_activated=%d\n", + info->is_hs_configured, info->hs_activated); + PRINTM(MERROR, "pps_uapsd_mode=%d sleep_pd=%d\n", info->pps_uapsd_mode, + info->sleep_pd); + PRINTM(MERROR, "tx_lock_flag = %d\n", info->tx_lock_flag); + PRINTM(MERROR, "port_open = %d\n", info->port_open); + PRINTM(MERROR, "scan_processing = %d\n", info->scan_processing); + for (i = 0; i < info->ralist_num; i++) { + PRINTM(MERROR, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + info->ralist[i].ra[0], info->ralist[i].ra[1], + info->ralist[i].ra[2], info->ralist[i].ra[3], + info->ralist[i].ra[4], info->ralist[i].ra[5], + info->ralist[i].tid, info->ralist[i].total_pkts, + info->ralist[i].tx_pause); + } + +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + PRINTM(MERROR, "txbd: rdptr=0x%x wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + PRINTM(MERROR, "rxbd: rdptr=0x%x wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + PRINTM(MERROR, "eventbd: rdptr=0x%x wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + } +#endif + PRINTM(MERROR, "------------mlan_debug_info End-------------\n"); + LEAVE(); +} + +/** + * @brief This function handle the shutdown timeout issue + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +void woal_ioctl_timeout(moal_handle *handle) +{ + moal_private *priv = NULL; + + ENTER(); + + PRINTM(MMSG, "woal_ioctl_timout.\n"); + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (priv) { + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + } + LEAVE(); + return; +} + +/** + * @brief This function handles the timeout of packet + * transmission + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void woal_tx_timeout(struct net_device *dev +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + , + unsigned int txqueue +#endif +) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + priv->num_tx_timeout++; + PRINTM(MERROR, "%lu : %s (bss=%d): Tx timeout (%d)\n", jiffies, + dev->name, priv->bss_index, priv->num_tx_timeout); + woal_set_trans_start(dev); + + if (priv->num_tx_timeout == NUM_TX_TIMEOUT_THRESHOLD) { + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_OR_UAP_CFG80211(priv->phandle->params.cfg80211_wext)) + woal_cfg80211_vendor_event(priv, event_hang, + CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#endif +#endif + priv->phandle->driver_status = MTRUE; + woal_process_hang(priv->phandle); + + wifi_status = 3; + } + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to net_device_stats structure + */ +struct net_device_stats *woal_get_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + return &priv->stats; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** + * @brief This function handles wmm queue select + * + * @param dev A pointer to net_device structure + * @param skb A pointer to sk_buff structure + * + * @return tx_queue index (0-3) + */ +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + , + struct net_device *sb_dev +#else + , + void *accel_priv +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + , + select_queue_fallback_t fallback +#endif +#endif +) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct ethhdr *eth = NULL; + t_u8 tid = 0; + t_u8 index = 0; + + ENTER(); + if (priv->phandle->surprise_removed == MTRUE) { + LEAVE(); + return index; + } + /* + * skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (IS_SKB_MAGIC_VLAN(skb)) { + tid = GET_VLAN_PRIO(skb); + } else { + eth = (struct ethhdr *)skb->data; + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET]; + if (tid == 0xFF) + tid = (IPTOS_PREC(SKB_TOS(skb)) >> + IPTOS_OFFSET); + break; + case __constant_htons(ETH_P_IPV6): + tid = SKB_TIDV6(skb); + break; + case __constant_htons(ETH_P_ARP): + default: + break; + } + } + + index = mlan_select_wmm_queue(priv->phandle->pmlan_adapter, + priv->bss_index, tid); + PRINTM(MDATA, "select queue: tid=%d, index=%d\n", tid, index); + LEAVE(); + return index; +} +#endif + +/** + * @brief This function flush tx status queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_flush_tx_stat_queue(moal_private *priv) +{ + struct tx_status_info *tx_info = NULL, *tmp_node; + unsigned long flags; + struct sk_buff *skb = NULL; + spin_lock_irqsave(&priv->tx_stat_lock, flags); + list_for_each_entry_safe (tx_info, tmp_node, &priv->tx_stat_queue, + link) { + list_del(&tx_info->link); + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + skb = (struct sk_buff *)tx_info->tx_skb; + if (tx_info->tx_cookie) { +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(priv->netdev, + tx_info->tx_cookie, skb->data, + skb->len, true, GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, tx_info->tx_cookie, + skb->data, skb->len, true, + GFP_ATOMIC); +#endif +#endif +#endif + } + dev_kfree_skb_any(skb); + kfree(tx_info); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + } + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + spin_lock_bh(&(priv->tx_q.lock)); + __skb_queue_purge(&priv->tx_q); + spin_unlock_bh(&(priv->tx_q.lock)); +} + +/** + * @brief This function gets tx info from tx_stat_queue + * + * @param priv A pointer to moal_private structure + * @param tx_seq_num tx seq number + * + * @return A pointer to the tcp tx_status_info structure, if found. + * Otherwise, null + */ +struct tx_status_info *woal_get_tx_info(moal_private *priv, t_u8 tx_seq_num) +{ + struct tx_status_info *tx_info = NULL; + ENTER(); + + list_for_each_entry (tx_info, &priv->tx_stat_queue, link) { + if (tx_info->tx_seq_num == tx_seq_num) { + LEAVE(); + return tx_info; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function remove tx info from queue + * + * @param priv A pointer to moal_private structure + * @param tx_seq_num tx seq number + * + * @return N/A + */ +void woal_remove_tx_info(moal_private *priv, t_u8 tx_seq_num) +{ + struct tx_status_info *tx_info, *tmp = NULL; + unsigned long flags; + ENTER(); + + spin_lock_irqsave(&priv->tx_stat_lock, flags); + list_for_each_entry_safe (tx_info, tmp, &priv->tx_stat_queue, link) { + if (tx_info->tx_seq_num == tx_seq_num) { + list_del(&tx_info->link); + dev_kfree_skb_any((struct sk_buff *)tx_info->tx_skb); + kfree(tx_info); + break; + } + } + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + + LEAVE(); +} + +/** + * @brief This function flush tcp session queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_flush_tcp_sess_queue(moal_private *priv) +{ + struct tcp_sess *tcp_sess = NULL, *tmp_node; + unsigned long flags; + struct sk_buff *skb; + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + list_for_each_entry_safe (tcp_sess, tmp_node, &priv->tcp_sess_queue, + link) { + list_del(&tcp_sess->link); + if (tcp_sess->is_timer_set) + woal_cancel_timer(&tcp_sess->ack_timer); + skb = (struct sk_buff *)tcp_sess->ack_skb; + if (skb) + dev_kfree_skb_any(skb); + kfree(tcp_sess); + } + INIT_LIST_HEAD(&priv->tcp_sess_queue); + priv->tcp_ack_drop_cnt = 0; + priv->tcp_ack_cnt = 0; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); +} + +/** + * @brief This function gets tcp session from the tcp session queue + * + * @param priv A pointer to moal_private structure + * @param src_ip IP address of the device + * @param src_port TCP port of the device + * @param dst_ip IP address of the client + * @param dst_port TCP port of the client + * + * @return A pointer to the tcp session data structure, if found. + * Otherwise, null + */ +static inline struct tcp_sess *woal_get_tcp_sess(moal_private *priv, + t_u32 src_ip, t_u16 src_port, + t_u32 dst_ip, t_u16 dst_port) +{ + struct tcp_sess *tcp_sess = NULL; + ENTER(); + + list_for_each_entry (tcp_sess, &priv->tcp_sess_queue, link) { + if ((tcp_sess->src_ip_addr == src_ip) && + (tcp_sess->src_tcp_port == src_port) && + (tcp_sess->dst_ip_addr == dst_ip) && + (tcp_sess->dst_tcp_port == dst_port)) { + LEAVE(); + return tcp_sess; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function send the holding tcp ack packet + * re-assoc thread. + * + * @param context A pointer to context + * @return N/A + */ +void woal_tcp_ack_timer_func(void *context) +{ + struct tcp_sess *tcp_session = (struct tcp_sess *)context; + moal_private *priv = (moal_private *)tcp_session->priv; + unsigned long flags; + mlan_buffer *pmbuf; + struct sk_buff *skb; + mlan_status status; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + ENTER(); + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + tcp_session->is_timer_set = MFALSE; + skb = (struct sk_buff *)tcp_session->ack_skb; + pmbuf = (mlan_buffer *)tcp_session->pmbuf; + tcp_session->ack_skb = NULL; + tcp_session->pmbuf = NULL; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + if (skb && pmbuf) { + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = netdev_get_tx_queue( + priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", + index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= + MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + queue_work(priv->phandle->workqueue, + &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } + } + LEAVE(); + return; +} + +/** + * @brief This function send the tcp ack + * + * + * @param priv A pointer to moal_private structure + * @param tcp_session A pointer to tcp_session + * @return N/A + */ +void woal_send_tcp_ack(moal_private *priv, struct tcp_sess *tcp_session) +{ + mlan_status status; + struct sk_buff *skb = (struct sk_buff *)tcp_session->ack_skb; + mlan_buffer *pmbuf = (mlan_buffer *)tcp_session->pmbuf; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + ENTER(); + if (tcp_session->is_timer_set) { + woal_cancel_timer(&tcp_session->ack_timer); + tcp_session->is_timer_set = MFALSE; + } + tcp_session->ack_skb = NULL; + tcp_session->pmbuf = NULL; + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } + LEAVE(); +} + +/** + * @brief This function get the tcp ack session node + * + * @param priv A pointer to moal_private structure + * @param pmbuf A pointer to mlan_buffer associated with a skb + * + * @return 1, if it's dropped; 0, if not dropped + */ +int woal_process_tcp_ack(moal_private *priv, mlan_buffer *pmbuf) +{ + int ret = 0; + unsigned long flags; + struct tcp_sess *tcp_session; + struct ethhdr *ethh = NULL; + struct iphdr *iph = NULL; + struct tcphdr *tcph = NULL; + t_u32 ack_seq; + struct sk_buff *skb; + + ENTER(); + + /** check the tcp packet */ + ethh = (struct ethhdr *)(pmbuf->pbuf + pmbuf->data_offset); + if (ntohs(ethh->h_proto) != ETH_P_IP) { + LEAVE(); + return 0; + } + iph = (struct iphdr *)((t_u8 *)ethh + sizeof(struct ethhdr)); + if (iph->protocol != IPPROTO_TCP) { + LEAVE(); + return 0; + } + tcph = (struct tcphdr *)((t_u8 *)iph + iph->ihl * 4); + + if (*((t_u8 *)tcph + 13) == 0x10) { + /* Only replace ACK */ + if (ntohs(iph->tot_len) > (iph->ihl + tcph->doff) * 4) { + /* Don't drop ACK with payload */ + /* TODO: should we delete previous TCP session */ + LEAVE(); + return ret; + } + priv->tcp_ack_cnt++; + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + tcp_session = woal_get_tcp_sess(priv, iph->saddr, tcph->source, + iph->daddr, tcph->dest); + if (!tcp_session) { + tcp_session = + kmalloc(sizeof(struct tcp_sess), GFP_ATOMIC); + if (!tcp_session) { + PRINTM(MERROR, "Fail to allocate tcp_sess.\n"); + spin_unlock_irqrestore(&priv->tcp_sess_lock, + flags); + goto done; + } + tcp_session->ack_skb = pmbuf->pdesc; + tcp_session->pmbuf = pmbuf; + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + tcp_session->src_ip_addr = iph->saddr; + tcp_session->dst_ip_addr = iph->daddr; + tcp_session->src_tcp_port = tcph->source; + tcp_session->dst_tcp_port = tcph->dest; + tcp_session->ack_seq = ntohl(tcph->ack_seq); + tcp_session->priv = (void *)priv; + skb = (struct sk_buff *)pmbuf->pdesc; + skb->cb[0] = 0; + /* Initialize the timer for tcp ack */ + woal_initialize_timer(&tcp_session->ack_timer, + woal_tcp_ack_timer_func, + tcp_session); + tcp_session->is_timer_set = MTRUE; + woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS); + list_add_tail(&tcp_session->link, + &priv->tcp_sess_queue); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + ret = HOLD_TCP_ACK; + LEAVE(); + return ret; + } else if (!tcp_session->ack_skb) { + tcp_session->ack_skb = pmbuf->pdesc; + tcp_session->pmbuf = pmbuf; + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + tcp_session->ack_seq = ntohl(tcph->ack_seq); + tcp_session->priv = (void *)priv; + skb = (struct sk_buff *)pmbuf->pdesc; + skb->cb[0] = 0; + tcp_session->is_timer_set = MTRUE; + woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + ret = HOLD_TCP_ACK; + LEAVE(); + return ret; + } + ack_seq = ntohl(tcph->ack_seq); + skb = (struct sk_buff *)tcp_session->ack_skb; + if (likely(ack_seq > tcp_session->ack_seq) && + (skb->len == pmbuf->data_len)) { + moal_memcpy_ext(priv->phandle, skb->data, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len, skb->len); + tcp_session->ack_seq = ack_seq; + ret = DROP_TCP_ACK; + skb->cb[0]++; +// We will drop 90% tcp ack +#define TCP_ACK_MAX_HOLD 9 + if (skb->cb[0] >= TCP_ACK_MAX_HOLD) + woal_send_tcp_ack(priv, tcp_session); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + skb = (struct sk_buff *)pmbuf->pdesc; + dev_kfree_skb_any(skb); + priv->tcp_ack_drop_cnt++; + } else { + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + LEAVE(); + return ret; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles packet transmission + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * + * @return 0 --success + */ +int woal_start_xmit(moal_private *priv, struct sk_buff *skb) +{ + mlan_buffer *pmbuf = NULL; + mlan_status status; + struct sk_buff *new_skb = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + int ret = 0; + + ENTER(); + + priv->num_tx_timeout = 0; + if (!skb->len || (skb->len > ETH_FRAME_LEN)) { + PRINTM(MERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + ETH_FRAME_LEN); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (skb->cloned || (skb_headroom(skb) < + (MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len))) { + PRINTM(MWARN, + "Tx: skb cloned %d or Insufficient skb headroom %d\n", + skb->cloned, skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = skb_realloc_headroom( + skb, MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len); + if (unlikely(!new_skb)) { + PRINTM(MERROR, "Tx: Cannot allocate skb\n"); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + PRINTM(MINFO, "new skb headroom %d\n", skb_headroom(skb)); + } + pmbuf = (mlan_buffer *)skb->head; + memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->bss_index = priv->bss_index; + woal_fill_mlan_buffer(priv, pmbuf, skb); + if (priv->enable_tcp_ack_enh == MTRUE) { + ret = woal_process_tcp_ack(priv, pmbuf); + if (ret) + goto done; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); +#endif + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + if (is_zero_timeval(priv->phandle->tx_time_start)) { + priv->phandle->tx_time_start.time_sec = + pmbuf->in_ts_sec; + priv->phandle->tx_time_start.time_usec = + pmbuf->in_ts_usec; + PRINTM(MINFO, "%s : start_timeval=%d:%d \n", __func__, + priv->phandle->tx_time_start.time_sec, + priv->phandle->tx_time_start.time_usec); + } + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + if (!mlan_is_main_process_running(priv->phandle->pmlan_adapter)) + queue_work(priv->phandle->workqueue, + &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } +done: + LEAVE(); + return 0; +} + +/** + * @brief This function handles packet transmission + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * + * @return 0 --success + */ +netdev_tx_t woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + PRINTM(MDATA, "%lu : %s (bss=%d): Data <= kernel\n", jiffies, dev->name, + priv->bss_index); + + /* Collect TP statistics */ + if (priv->phandle->tp_acnt.on) + moal_tp_accounting(priv->phandle, skb, 1); + /* Drop Tx packets at drop point 1 */ + if (priv->phandle->tp_acnt.drop_point == 1) { + dev_kfree_skb_any(skb); + LEAVE(); + return 0; + } + if (priv->phandle->surprise_removed == MTRUE) { + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (moal_extflg_isset(priv->phandle, EXT_TX_WORK)) { + spin_lock_bh(&(priv->tx_q.lock)); + __skb_queue_tail(&(priv->tx_q), skb); + spin_unlock_bh(&(priv->tx_q.lock)); + + queue_work(priv->phandle->tx_workqueue, + &priv->phandle->tx_work); + goto done; + } + woal_start_xmit(priv, skb); +done: + LEAVE(); + return 0; +} + +/** + * @brief Convert ascii string to Hex integer + * + * @param d A pointer to integer buf + * @param s A pointer to ascii string + * @param dlen The byte number of ascii string in hex + * + * @return Number of integer + */ +int woal_ascii2hex(t_u8 *d, char *s, t_u32 dlen) +{ + unsigned int i; + t_u8 n; + + ENTER(); + + memset(d, 0x00, dlen); + + for (i = 0; i < dlen * 2; i++) { + if ((s[i] >= 48) && (s[i] <= 57)) + n = s[i] - 48; + else if ((s[i] >= 65) && (s[i] <= 70)) + n = s[i] - 55; + else if ((s[i] >= 97) && (s[i] <= 102)) + n = s[i] - 87; + else + break; + if (!(i % 2)) + n = n * 16; + d[i / 2] += n; + } + + LEAVE(); + return i; +} + +/** + * @brief Return integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_atoi(int *data, char *a) +{ + int i, val = 0, len; + int mul = 1; + + ENTER(); + + len = strlen(a); + if (len > 2) { + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = woal_atox(a); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + if ((i == 0) && (a[i] == '-')) { + mul = -1; + } else { + PRINTM(MERROR, "Invalid char %c in string %s\n", + a[i], a); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + } + *data = (mul * val); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Return hex value of a given ascii string + * + * @param a String to be converted to ascii + * + * @return The converted character if a is a valid hex, else 0 + */ +int woal_atox(char *a) +{ + int i = 0; + + ENTER(); + + while (isxdigit(*a)) + i = i * 16 + woal_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +char *woal_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Convert mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +void woal_mac2u8(t_u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + moal_memcpy_ext(NULL, mac_buff, buf, strlen(buf), strlen(buf) + 1); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = woal_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = woal_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief This function sets multicast addresses to firmware + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void woal_set_multicast_list(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + woal_request_set_multicast_list(priv, dev); + LEAVE(); +} +#endif + +/** + * @brief This function initializes the private structure + * and set default value to the member of moal_private. + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return N/A + */ +void woal_init_priv(moal_private *priv, t_u8 wait_option) +{ + ENTER(); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->current_key_index = 0; + priv->rate_index = AUTO_RATE; + priv->is_adhoc_link_sensed = MFALSE; + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + memset(&priv->nick_name, 0, sizeof(priv->nick_name)); + priv->num_tx_timeout = 0; + priv->rx_filter = 0; + +#ifdef REASSOCIATION + priv->reassoc_on = MFALSE; + priv->set_asynced_essid_flag = MFALSE; +#endif +#ifdef STA_CFG80211 + memset(&priv->sme_current, 0, + sizeof(struct cfg80211_connect_params)); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_init_wifi_hal(priv); +#endif + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->bss_started = MFALSE; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def)); + memset(&priv->csa_chan, 0, sizeof(struct cfg80211_chan_def)); + priv->uap_tx_blocked = MFALSE; + memset(&priv->beacon_after, 0, + sizeof(struct cfg80211_beacon_data)); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_init_wifi_hal(priv); +#endif +#endif + } +#endif + + skb_queue_head_init(&priv->tx_q); + memset(&priv->tx_protocols, 0, sizeof(dot11_protocol)); + memset(&priv->rx_protocols, 0, sizeof(dot11_protocol)); + priv->media_connected = MFALSE; + + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +#endif +#ifdef STA_SUPPORT +#endif + + priv->enable_tcp_ack_enh = MTRUE; + + priv->gtk_data_ready = MFALSE; + memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data)); + + woal_request_get_fw_info(priv, wait_option, NULL); + + /* Set MAC address from the insmod command line */ + if (priv->phandle->set_mac_addr) { + memset(priv->current_addr, 0, ETH_ALEN); + moal_memcpy_ext(priv->phandle, priv->current_addr, + priv->phandle->mac_addr, ETH_ALEN, ETH_ALEN); + } + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +#ifdef MFG_CMD_SUPPORT + if (priv->phandle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (priv->bss_virtual) { + if (priv->pa_netdev) { + moal_memcpy_ext( + priv->phandle, + priv->current_addr, + priv->pa_netdev->dev_addr, + ETH_ALEN, ETH_ALEN); + priv->current_addr[4] ^= 0x80; + if (priv->phandle->second_mac) + priv->current_addr[5] ^= 0x80; + PRINTM(MCMND, + "Set WFD interface addr: " MACSTR + "\n", + MAC2STR(priv->current_addr)); + } + } else { + priv->current_addr[0] |= 0x02; + PRINTM(MCMND, + "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } + } +#endif +#endif +#endif + + /* Set MAC address for UAPx/MLANx/WFDx/OCBx and let them + * different with each other */ +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) +#endif + { + priv->current_addr[4] += priv->bss_index; + PRINTM(MCMND, "Set %s interface addr: " MACSTR "\n", + priv->netdev->name, MAC2STR(priv->current_addr)); + } + + woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT); + moal_memcpy_ext(priv->phandle, priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN, ETH_ALEN); + +#ifdef UAP_SUPPORT + priv->user_cac_period_msec = 0; +#endif + LEAVE(); +} + +/** + * @brief Reset all interfaces if all_intf flag is TRUE, + * otherwise specified interface only + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param all_intf TRUE : all interfaces + * FALSE : current interface only + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +int woal_reset_intf(moal_private *priv, t_u8 wait_option, int all_intf) +{ + int ret = MLAN_STATUS_SUCCESS; + int intf_num; + moal_handle *handle = NULL; + mlan_bss_info bss_info; + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle = priv->phandle; + + if (handle->rf_test_mode) + woal_process_rf_test_mode(handle, MFG_CMD_UNSET_TEST_MODE); +#ifdef STA_SUPPORT + woal_cancel_scan(priv, wait_option); +#endif + + /* Stop queue and detach device */ + if (!all_intf) { + woal_stop_queue(priv->netdev); + netif_device_detach(priv->netdev); + } else { + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_stop_queue(handle->priv[intf_num]->netdev); + netif_device_detach(handle->priv[intf_num]->netdev); + } + } + + /* Get BSS info */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, wait_option, &bss_info); + + /* Cancel host sleep */ + if (bss_info.is_hs_configured) { + if (MLAN_STATUS_SUCCESS != woal_cancel_hs(priv, wait_option)) { + ret = -EFAULT; + goto done; + } + } + + /* Disconnect from network */ + if (!all_intf) { + /* Disconnect specified interface only */ + if ((priv->media_connected == MTRUE) +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) +#endif + ) { + woal_disconnect(priv, wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + priv->media_connected = MFALSE; + } + } else { + /* Disconnect all interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + if (handle->priv[intf_num]->media_connected == MTRUE +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP) +#endif + ) { + woal_disconnect(handle->priv[intf_num], + wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + handle->priv[intf_num]->media_connected = + MFALSE; + } + } + } + +#ifdef REASSOCIATION + /* Reset the reassoc timer and status */ + if (!all_intf) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + } else { + handle->reassoc_on = 0; + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + handle->priv[intf_num]->reassoc_on = MFALSE; + handle->priv[intf_num]->set_asynced_essid_flag = MFALSE; + } + } + if (!handle->reassoc_on && handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } +#endif /* REASSOCIATION */ + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + if (handle->is_go_timer_set) { + woal_cancel_timer(&handle->go_timer); + handle->is_go_timer_set = MFALSE; + } +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } +#endif +#endif + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function return the point to structure moal_private + * + * @param handle Pointer to structure moal_handle + * @param bss_index BSS index number + * + * @return moal_private pointer or NULL + */ +moal_private *woal_bss_index_to_priv(moal_handle *handle, t_u8 bss_index) +{ + int i; + + ENTER(); + if (!handle) { + LEAVE(); + return NULL; + } + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (handle->priv[i] && + (handle->priv[i]->bss_index == bss_index)) { + LEAVE(); + return handle->priv[i]; + } + } + + LEAVE(); + return NULL; +} + +/** + * @brief This function alloc mlan_buffer. + * @param handle A pointer to moal_handle structure + * @param size buffer size to allocate + * + * @return mlan_buffer pointer or NULL + */ +pmlan_buffer woal_alloc_mlan_buffer(moal_handle *handle, int size) +{ + mlan_buffer *pmbuf = NULL; + struct sk_buff *skb; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + if (size <= 0) { + PRINTM(MERROR, "Buffer size must be positive\n"); + LEAVE(); + return NULL; + } + + skb = __dev_alloc_skb(size + sizeof(mlan_buffer), flag); + if (!skb) { + PRINTM(MERROR, "%s: No free skb\n", __func__); + LEAVE(); + return NULL; + } + skb_reserve(skb, sizeof(mlan_buffer)); + pmbuf = (mlan_buffer *)skb->head; + memset((u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->pdesc = (t_void *)skb; + pmbuf->pbuf = (t_u8 *)skb->data; + atomic_inc(&handle->mbufalloc_count); + LEAVE(); + return pmbuf; +} + +/** + * @brief This function alloc mlan_ioctl_req. + * + * @param size buffer size to allocate + * + * @return mlan_ioctl_req pointer or NULL + */ +pmlan_ioctl_req woal_alloc_mlan_ioctl_req(int size) +{ + mlan_ioctl_req *req = NULL; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + req = kzalloc((sizeof(mlan_ioctl_req) + size + sizeof(int) + + sizeof(wait_queue)), + flag); + if (!req) { + PRINTM(MERROR, "%s: Fail to alloc ioctl buffer\n", __func__); + LEAVE(); + return NULL; + } + req->pbuf = (t_u8 *)req + sizeof(mlan_ioctl_req) + sizeof(wait_queue); + req->buf_len = (t_u32)size; + req->reserved_1 = (t_ptr)((t_u8 *)req + sizeof(mlan_ioctl_req)); + + LEAVE(); + return req; +} + +/** + * @brief This function frees mlan_buffer. + * @param handle A pointer to moal_handle structure + * @param pmbuf Pointer to mlan_buffer + * + * @return N/A + */ +void woal_free_mlan_buffer(moal_handle *handle, pmlan_buffer pmbuf) +{ + ENTER(); + if (!pmbuf) { + LEAVE(); + return; + } + if (pmbuf->pdesc) + dev_kfree_skb_any((struct sk_buff *)pmbuf->pdesc); + else + PRINTM(MERROR, "free mlan buffer without pdesc\n"); + atomic_dec(&handle->mbufalloc_count); + LEAVE(); + return; +} + +/** + * @brief This function get card info from card type + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +static int woal_get_card_info(moal_handle *phandle) +{ + int ret = 0; + + ENTER(); + + switch (phandle->card_type) { +#ifdef SD8887 + case CARD_TYPE_SD8887: + phandle->card_info = &card_info_SD8887; + break; +#endif +#if defined(SD8897) + case CARD_TYPE_SD8897: + phandle->card_info = &card_info_SD8897; + break; +#endif +#if defined(PCIE8897) + case CARD_TYPE_PCIE8897: + phandle->card_info = &card_info_PCIE8897; + break; +#endif +#if defined(USB8897) + case CARD_TYPE_USB8897: + phandle->card_info = &card_info_USB8897; + break; +#endif +#ifdef SD8977 + case CARD_TYPE_SD8977: + phandle->card_info = &card_info_SD8977; + break; +#endif +#ifdef SD8978 + case CARD_TYPE_SD8978: + phandle->card_info = &card_info_SD8978; + break; +#endif +#ifdef SD8997 + case CARD_TYPE_SD8997: + phandle->card_info = &card_info_SD8997; + break; +#endif +#ifdef SD9098 + case CARD_TYPE_SD9098: + phandle->card_info = &card_info_SD9098; + break; +#endif +#ifdef SD9097 + case CARD_TYPE_SD9097: + phandle->card_info = &card_info_SD9097; + break; +#endif +#ifdef PCIE8997 + case CARD_TYPE_PCIE8997: + phandle->card_info = &card_info_PCIE8997; + break; +#endif +#ifdef PCIE9097 + case CARD_TYPE_PCIE9097: + phandle->card_info = &card_info_PCIE9097; + break; +#endif +#ifdef PCIE9098 + case CARD_TYPE_PCIE9098: + phandle->card_info = &card_info_PCIE9098; + phandle->event_fw_dump = MTRUE; + break; +#endif +#ifdef USB8997 + case CARD_TYPE_USB8997: + phandle->card_info = &card_info_USB8997; + break; +#endif +#ifdef USB8978 + case CARD_TYPE_USB8978: + phandle->card_info = &card_info_USB8978; + break; +#endif +#ifdef USB9098 + case CARD_TYPE_USB9098: + phandle->card_info = &card_info_USB9098; + break; +#endif +#ifdef USB9097 + case CARD_TYPE_USB9097: + phandle->card_info = &card_info_USB9097; + break; +#endif +#ifdef SD8987 + case CARD_TYPE_SD8987: + phandle->card_info = &card_info_SD8987; + break; +#endif + default: + PRINTM(MERROR, + "woal_get_card_info can't get right card type \n"); + ret = -1; + break; + } + + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +#endif /* STA_SUPPORT */ + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to moal_private structure + * @param payload A pointer to payload buffer + * @param len Length of the payload + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_broadcast_event(moal_private *priv, t_u8 *payload, t_u32 len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + moal_handle *handle = priv->phandle; + struct net_device *netdev = priv->netdev; + struct sock *sk = handle->nl_sk; + + ENTER(); + + /* interface name to be prepended to event */ + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD +#ifdef WIFI_DIRECT_SUPPORT + * 2 +#endif + ) { + PRINTM(MERROR, "event size is too big, len=%d\n", (int)len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (sk) { + /* Allocate skb */ +#ifdef WIFI_DIRECT_SUPPORT + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD) { + skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD * 2), + GFP_ATOMIC); + if (!skb) { + PRINTM(MERROR, + "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { +#endif + skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), + GFP_ATOMIC); + if (!skb) { + PRINTM(MERROR, + "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef WIFI_DIRECT_SUPPORT + } +#endif + memset(skb->data, 0, NLMSG_SPACE(NL_MAX_PAYLOAD)); + + nlh = (struct nlmsghdr *)skb->data; + nlh->nlmsg_len = NLMSG_SPACE(len + IFNAMSIZ); + + /* From kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + + /* Data */ + skb_put(skb, nlh->nlmsg_len); + moal_memcpy_ext(handle, NLMSG_DATA(nlh), netdev->name, IFNAMSIZ, + nlh->nlmsg_len - NLMSG_LENGTH(0)); + moal_memcpy_ext(handle, ((t_u8 *)(NLMSG_DATA(nlh))) + IFNAMSIZ, + payload, len, + nlh->nlmsg_len - NLMSG_LENGTH(IFNAMSIZ)); + + /* From Kernel */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + NETLINK_CB(skb).pid = 0; +#else + NETLINK_CB(skb).portid = 0; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + /* Multicast message */ + NETLINK_CB(skb).dst_pid = 0; +#endif + + /* Multicast group number */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + NETLINK_CB(skb).dst_groups = NL_MULTICAST_GROUP; +#else + NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; +#endif + + /* Send message */ + ret = netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, + GFP_ATOMIC); + if (ret) { + PRINTM(MWARN, "netlink_broadcast failed: ret=%d\n", + ret); + goto done; + } + + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, + "Could not send event through NETLINK. Link down.\n"); + ret = MLAN_STATUS_FAILURE; + } +done: + LEAVE(); + return ret; +} + +#ifdef REASSOCIATION +/** + * @brief This function handles re-association. it is triggered + * by re-assoc timer. + * + * @param data A pointer to wlan_thread structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int woal_reassociation_thread(void *data) +{ + moal_thread *pmoal_thread = data; + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)pmoal_thread->handle; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + int i; + BOOLEAN reassoc_timer_req; + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; + mlan_status status; + mlan_bss_info bss_info; + t_u32 timer_val = MOAL_TIMER_10S; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + ENTER(); + + woal_activate_thread(pmoal_thread); + init_waitqueue_entry(&wait, current); + + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&pmoal_thread->wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + schedule(); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&pmoal_thread->wait_q, &wait); +#if defined(USB) + if (IS_USB(handle->card_type)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13) + try_to_freeze(0); /* Argument is not used by the kernel + */ +#else + try_to_freeze(); +#endif + } +#endif + + /* Cancel re-association timer */ + if (handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + + if (handle->surprise_removed) + break; + if (kthread_should_stop()) + break; + + if (handle->hardware_status != HardwareStatusReady) { + PRINTM(MINFO, + "Reassoc: Hardware status is not correct\n"); + continue; + } + + PRINTM(MEVENT, "Reassoc: Thread waking up...\n"); + reassoc_timer_req = MFALSE; +#ifdef STA_CFG80211 + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM) && + (priv = handle->priv[i]); + i++) { + if (priv->roaming_required) { + priv->roaming_required = MFALSE; + PRINTM(MEVENT, "Try to roaming......\n"); + woal_start_roaming(priv); + break; + } + } +#endif + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM) && + (priv = handle->priv[i]); + i++) { + if (priv->reassoc_required == MFALSE) { + priv->set_asynced_essid_flag = MFALSE; + continue; + } + + memset(&bss_info, 0x00, sizeof(bss_info)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, + &bss_info)) { + PRINTM(MINFO, "Ressoc: Fail to get bss info\n"); + priv->reassoc_required = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + continue; + } + + if (bss_info.bss_mode != MLAN_BSS_MODE_INFRA || + priv->media_connected != MFALSE) { + PRINTM(MINFO, + "Reassoc: ad-hoc mode or media connected\n"); + priv->reassoc_required = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + continue; + } + /** avoid on going scan from other thread */ + if (handle->scan_pending_on_block) { + reassoc_timer_req = MTRUE; + break; + } + + /* The semaphore is used to avoid reassociation + thread and wlan_set_scan/wlan_set_essid + interrupting each other. Reassociation should + be disabled completely by application if + wlan_set_user_scan_ioctl/wlan_set_wap is + used. + */ + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, + "Acquire semaphore error, reassociation thread\n"); + reassoc_timer_req = MTRUE; + break; + } + + PRINTM(MINFO, "Reassoc: Required ESSID: %s\n", + priv->prev_ssid_bssid.ssid.ssid); + PRINTM(MINFO, "Reassoc: Performing Active Scan\n"); + + memset(&req_ssid, 0x00, sizeof(mlan_802_11_ssid)); + moal_memcpy_ext(priv->phandle, &req_ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + PRINTM(MERROR, + "Reassoc: Fail to do specific scan\n"); + reassoc_timer_req = MTRUE; + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + break; + } + + if (handle->surprise_removed) { + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + break; + } + + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (priv->set_asynced_essid_flag == MTRUE) { + if (priv->assoc_with_mac && + memcmp(priv->prev_ssid_bssid.bssid, + zero_mac, MLAN_MAC_ADDR_LENGTH)) { + /* Search AP by BSSID & SSID */ + PRINTM(MINFO, + "Reassoc: Search AP by BSSID & SSID\n"); + moal_memcpy_ext( + priv->phandle, + &ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); + } else { + /* Search AP by ESSID for asynced essid + * setting */ + PRINTM(MINFO, + "Set asynced essid: Search AP by ESSID\n"); + } + + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + } else { + /* Search AP by BSSID first */ + PRINTM(MINFO, + "Reassoc: Search AP by BSSID first\n"); + moal_memcpy_ext(priv->phandle, + &ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); + } + + status = woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid); +#ifdef STA_WEXT + if (status == MLAN_STATUS_SUCCESS) { + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "Reassoc: The AP's channel is invalid for current region\n"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + /** The find AP without ssid, we need re-search + */ + if (status == MLAN_STATUS_SUCCESS && + !ssid_bssid.ssid.ssid_len) { + PRINTM(MINFO, + "Reassoc: Skip AP without ssid\n"); + status = MLAN_STATUS_FAILURE; + } + + if (priv->set_asynced_essid_flag != MTRUE && + MLAN_STATUS_SUCCESS != status) { + PRINTM(MINFO, + "Reassoc: AP not found in scan list\n"); + PRINTM(MINFO, "Reassoc: Search AP by SSID\n"); + /* Search AP by SSID */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + status = woal_find_best_network( + priv, MOAL_IOCTL_WAIT, &ssid_bssid); +#ifdef STA_WEXT + if (status == MLAN_STATUS_SUCCESS) { + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel( + priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "Reassoc: The AP's channel is invalid for current region\n"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + } + + if (status == MLAN_STATUS_SUCCESS) { + /* set the wep key */ + if (bss_info.wep_status) + woal_enable_wep_key(priv, + MOAL_IOCTL_WAIT); + /* Zero SSID implies use BSSID to + * connect */ + memset(&ssid_bssid.ssid, 0, + sizeof(mlan_802_11_ssid)); + status = woal_bss_start(priv, MOAL_IOCTL_WAIT, + &ssid_bssid); + } + + if (priv->media_connected == MFALSE) + reassoc_timer_req = MTRUE; + else { + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + + reassoc_timer_req = MFALSE; + if (priv->set_asynced_essid_flag == MTRUE) { + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + PRINTM(MINFO, + "Set asynced essid: Fail to get bss info after assoc\n"); + } else { + moal_memcpy_ext( + priv->phandle, + &priv->prev_ssid_bssid + .ssid, + &bss_info.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + moal_memcpy_ext( + priv->phandle, + &priv->prev_ssid_bssid + .bssid, + &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(priv->prev_ssid_bssid + .bssid)); + } + priv->set_asynced_essid_flag = MFALSE; + } + if (priv->rate_index != AUTO_RATE) { + req = woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_rate)); + + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = + MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + + req->action = MLAN_ACT_SET; + + rate->param.rate_cfg.rate = + priv->rate_index; + + status = woal_request_ioctl( + priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + if (status != + MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + kfree(req); + } + } + + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + } + + if (handle->surprise_removed) + break; + + if (reassoc_timer_req == MTRUE) { + handle->is_reassoc_timer_set = MTRUE; + if (priv && (priv->set_asynced_essid_flag == MTRUE)) { + PRINTM(MERROR, + "Set Async ESSID: No AP found or assoc failed.\n"); + priv->set_asynced_essid_flag = MFALSE; + } else { + PRINTM(MEVENT, + "Reassoc: No AP found or assoc failed. " + "Restarting re-assoc Timer: %d\n", + (int)timer_val); + woal_mod_timer(&handle->reassoc_timer, + timer_val); + } + } else { + if (priv) { + priv->set_asynced_essid_flag = MFALSE; + } + } + } + woal_deactivate_thread(pmoal_thread); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function triggers re-association by waking up + * re-assoc thread. + * + * @param context A pointer to context + * @return N/A + */ +void woal_reassoc_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + + ENTER(); + + PRINTM(MINFO, "reassoc_timer fired.\n"); + handle->is_reassoc_timer_set = MFALSE; + + PRINTM(MINFO, "Waking Up the Reassoc Thread\n"); + wake_up_interruptible(&handle->reassoc_thread.wait_q); + + LEAVE(); + return; +} +#endif /* REASSOCIATION */ + +#ifdef STA_SUPPORT +/** + * @brief update dscp mapping from assoc_resp/reassoc_resp + * + * @param priv Pointer to the moal_private driver data struct + * + * @return N/A + */ +void woal_update_dscp_mapping(moal_private *priv) +{ + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + IEEEtypes_Header_t *qos_mapping_ie = NULL; + DSCP_Range_t *pdscp_range = NULL; + t_u8 dscp_except_num = 0; + DSCP_Exception_t dscp_except[MAX_DSCP_EXCEPTION_NUM]; + int i, j; + ENTER(); + + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_NO_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp.assoc_resp_buf; + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + qos_mapping_ie = (IEEEtypes_Header_t *)woal_parse_ie_tlv( + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - ASSOC_RESP_FIXED_SIZE, QOS_MAPPING); + if (qos_mapping_ie && + (qos_mapping_ie->len >= (sizeof(DSCP_Range_t) * MAX_NUM_TID))) { + dscp_except_num = (qos_mapping_ie->len - + sizeof(DSCP_Range_t) * MAX_NUM_TID) / + sizeof(DSCP_Exception_t); + if (dscp_except_num > MAX_DSCP_EXCEPTION_NUM) { + PRINTM(MERROR, "dscp_except_num exceeds MAX limit\n"); + LEAVE(); + return; + } + moal_memcpy_ext(priv->phandle, dscp_except, + (t_u8 *)qos_mapping_ie + + sizeof(IEEEtypes_Header_t), + dscp_except_num * sizeof(DSCP_Exception_t), + sizeof(dscp_except)); + pdscp_range = + (DSCP_Range_t *)((t_u8 *)qos_mapping_ie + + sizeof(IEEEtypes_Header_t) + + dscp_except_num * + sizeof(DSCP_Exception_t)); + for (i = 0; i < MAX_NUM_TID; i++) { + PRINTM(MEVENT, "TID %d: dscp_low=%d, dscp_high=%d\n", i, + pdscp_range->dscp_low_value, + pdscp_range->dscp_high_value); + if (pdscp_range->dscp_low_value != 0xff && + pdscp_range->dscp_high_value != 0xff && + pdscp_range->dscp_high_value <= 63) { + for (j = pdscp_range->dscp_low_value; + j <= pdscp_range->dscp_high_value; j++) + priv->dscp_map[j] = i; + } + pdscp_range++; + } + for (i = 0; i < dscp_except_num; i++) { + if ((dscp_except[i].dscp_value <= 63) && + (dscp_except[i].user_priority <= 7)) { + PRINTM(MEVENT, + "dscp excpt: value=%d priority=%d\n", + dscp_except[i].dscp_value, + dscp_except[i].user_priority); + priv->dscp_map[dscp_except[i].dscp_value] = + dscp_except[i].user_priority; + } + } + } + LEAVE(); +} + +/** + * @brief Sends disconnect event + * + * @param priv A pointer to moal_private struct + * @param disconnect_reason disconnect reason code + * @return N/A + */ +t_void woal_send_disconnect_to_system(moal_private *priv, + t_u16 disconnect_reason) +{ + int custom_len = 0; + t_u8 event_buf[32]; +#ifdef STA_WEXT + union iwreq_data wrqu; +#endif +#ifdef STA_CFG80211 + unsigned long flags; +#endif + int cfg80211_wext = priv->phandle->params.cfg80211_wext; +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + mlan_ds_misc_gtk_rekey_data zero_gtk; +#endif +#endif + t_u16 reason_code = 0; + + ENTER(); + priv->media_connected = MFALSE; + if (!disconnect_reason) + reason_code = MLAN_REASON_DEAUTH_LEAVING; + else + reason_code = disconnect_reason; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_flush_tcp_sess_queue(priv); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + priv->gtk_data_ready = MFALSE; + memset(&zero_gtk, 0x00, sizeof(zero_gtk)); + if (priv->phandle->params.gtk_rekey_offload == + GTK_REKEY_OFFLOAD_ENABLE && + memcmp(&priv->gtk_rekey_data, &zero_gtk, + sizeof(priv->gtk_rekey_data)) != 0) { + PRINTM(MCMND, "clear GTK in woal_send_disconnect_to_system\n"); + woal_set_rekey_data(priv, NULL, MLAN_ACT_CLEAR, MOAL_NO_WAIT); + } + memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data)); +#endif +#endif + +#ifdef STA_CFG80211 + if (priv->bss_type == MLAN_BSS_TYPE_STA && + IS_STA_CFG80211(cfg80211_wext)) { + woal_flush_pmksa_list(priv); + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } +#endif + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_hist_data_reset(priv); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL); + } +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + spin_lock_irqsave(&priv->connect_lock, flags); + if (!priv->cfg_disconnect && !priv->cfg_connect && priv->wdev && + priv->wdev->current_bss) { + PRINTM(MMSG, + "wlan: Disconnected from " MACSTR + ": Reason code %d\n", + MAC2STR(priv->cfg_bssid), reason_code); + spin_unlock_irqrestore(&priv->connect_lock, flags); + priv->cfg_disconnect = MTRUE; + /* This function must be called only when + disconnect issued by the FW, i.e. + disconnected by AP. For IBSS mode this call + is not valid */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (priv->host_mlme) + woal_host_mlme_disconnect(priv, reason_code, + NULL); + else +#endif + cfg80211_disconnected(priv->netdev, reason_code, + NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + false, +#endif + GFP_KERNEL); + } else { + spin_unlock_irqrestore(&priv->connect_lock, flags); + } + if (!woal_is_any_interface_active(priv->phandle)) + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + priv->ft_ie_len = 0; + priv->ft_pre_connect = MFALSE; + priv->ft_md = 0; + priv->ft_cap = 0; + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + } +#endif /* STA_CFG80211 */ + + memset(event_buf, 0, sizeof(event_buf)); + custom_len = strlen(CUS_EVT_AP_CONNECTED); + strncpy(event_buf, CUS_EVT_AP_CONNECTED, + MIN((sizeof(event_buf) - 1), custom_len)); + woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN); + LEAVE(); +} +#endif /* STA_SUPPORT */ + +#if defined(PCIE) +/** + * @brief This function stores the SSU dumps in a file + * + * @param phandle A pointer to moal_handle + * @param pmevent A pointer to mlan_event structure + * + * @return N/A + */ +t_void woal_store_ssu_dump(moal_handle *phandle, mlan_event *pmevent) +{ + struct dentry *dentry; + struct path path; + struct file *pfile_ssudump = NULL; + char dw_string[10]; + loff_t pos = 0; + t_u32 i; + t_u32 *tmpbuf; + + ENTER(); + dentry = kern_path_create(AT_FDCWD, "/data", &path, 1); + if (IS_ERR(dentry)) { + goto save_ssudump; + } + vfs_mkdir(path.dentry->d_inode, dentry, 0777); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + mutex_unlock(&path.dentry->d_inode->i_mutex); +#else + inode_unlock(path.dentry->d_inode); +#endif + +save_ssudump: + pfile_ssudump = filp_open("/data/ssudump.txt", + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_ssudump)) { + PRINTM(MERROR, "Cannot create ssu dump file\n"); + LEAVE(); + return; + } + DBG_HEXDUMP(MEVT_D, "SSU addr", pmevent->event_buf, 8); + moal_memcpy_ext(phandle, &tmpbuf, pmevent->event_buf, sizeof(t_ptr), + sizeof(t_ptr)); + PRINTM(MEVENT, "woal_store_ssu_dump: tmpbuf %p\n", tmpbuf); + for (i = 0; i < pmevent->event_len / 4; i++) { + if ((i + 1) % 8 == 0) + snprintf(dw_string, sizeof(dw_string), "%08x\n", + *tmpbuf); + else + snprintf(dw_string, sizeof(dw_string), "%08x ", + *tmpbuf); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_ssudump, dw_string, 9, &pos); +#else + kernel_write(pfile_ssudump, dw_string, 9, &pos); +#endif + tmpbuf++; + } + filp_close(pfile_ssudump, NULL); + LEAVE(); + return; +} +#endif /* SSU_SUPPORT */ + +#define OFFSET_SEQNUM 4 +#define OFFSET_TYPE 8 +#define DUMP_TYPE_ENDE 2 +t_void woal_store_firmware_dump(moal_handle *phandle, mlan_event *pmevent) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + t_u16 seqnum; + t_u16 type = 0; + t_u8 path_name[64]; + moal_handle *ref_handle = NULL; + + ENTER(); + if (phandle->fwdump_fname) + pfile_fwdump = filp_open(phandle->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + else { + seqnum = woal_le16_to_cpu( + *(t_u16 *)(pmevent->event_buf + OFFSET_SEQNUM)); + type = woal_le16_to_cpu( + *(t_u16 *)(pmevent->event_buf + OFFSET_TYPE)); + + if (seqnum == 1) { + if (drvdbg & MFW_D) + drvdbg &= ~MFW_D; + phandle->fw_dump_len = 0; + PRINTM(MMSG, + "==== Start Receive FW dump event ====\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directort*/ + woal_create_dump_dir(phandle, path_name, + sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Firmware Dump directory name is %s\n", + path_name); + ref_handle = (moal_handle *)phandle->pref_mac; + if (ref_handle) + woal_dump_drv_info(ref_handle, path_name); + woal_dump_drv_info(phandle, path_name); + if (fwdump_fname) { + memset(fwdump_fname, 0, 64); + } else { + gfp_t flag; + flag = (in_atomic() || irqs_disabled()) ? + GFP_ATOMIC : + GFP_KERNEL; + fwdump_fname = kzalloc(64, flag); + } + sprintf(fwdump_fname, "%s/file_fwdump", path_name); + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + memset(fwdump_fname, 0, 64); + sprintf(fwdump_fname, "%s/%s", "/var", + "file_fwdump"); + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, + 0644); + } + } else + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + if (IS_ERR(pfile_fwdump)) { + PRINTM(MERROR, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } + phandle->fw_dump_len += pmevent->event_len - OFFSET_SEQNUM; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_fwdump, pmevent->event_buf + OFFSET_SEQNUM, + pmevent->event_len - OFFSET_SEQNUM, &pos); +#else + kernel_write(pfile_fwdump, pmevent->event_buf + OFFSET_SEQNUM, + pmevent->event_len - OFFSET_SEQNUM, &pos); +#endif + filp_close(pfile_fwdump, NULL); + if (type == DUMP_TYPE_ENDE) + PRINTM(MMSG, "==== FW DUMP END: %ld bytes ====\n", + (long int)phandle->fw_dump_len); + LEAVE(); + return; +} + +#define DRV_INFO_SIZE 0x60000 +#define DRV_INFO_PER_INTF 0x11000 +#define ROW_SIZE_16 16 +#define ROW_SIZE_32 32 + +/** + * @brief This function dump hex to file + * + * @param phandle A pointer to moal_handle + * @param buf A pointer to buffer to dump + * @param len lengh of buf + * @param ascii Whether add ascii at the end + * @param save_buf Buffer which is saved to + * + * @return The length of this log + */ +static int woal_save_hex_dump(int rowsize, const void *buf, size_t len, + bool ascii, t_u8 *save_buf) +{ + const u8 *ptr = buf; + int i, linelen, remaining = len; + unsigned char linebuf[ROW_SIZE_32 * 3 + 2 + ROW_SIZE_32 + 1]; + char *pos = (char *)save_buf; + + if (rowsize != ROW_SIZE_16 && rowsize != ROW_SIZE_32) + rowsize = ROW_SIZE_16; + + for (i = 0; i < len; i += rowsize) { + linelen = min(remaining, rowsize); + remaining -= rowsize; + + hex_dump_to_buffer(ptr + i, linelen, rowsize, 1, linebuf, + sizeof(linebuf), false); + + pos += sprintf(pos, "%s\n", linebuf); + } + + return pos - (char *)save_buf; +} + +/** + * @brief This function save moal_priv's debug log + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_dump_priv_drv_info(moal_handle *handle, t_u8 *buf) +{ + char *ptr = (char *)buf; + int index; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + int i = 0; +#endif + moal_private *priv; + + ENTER(); + if (!handle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + for (index = 0; index < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); + index++) { + priv = handle->priv[index]; + if (priv) { + ptr += sprintf(ptr, "[Interface : %s]\n", + priv->proc_entry_name); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + ptr += sprintf(ptr, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + ptr += sprintf(ptr, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + ptr += sprintf(ptr, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + ptr += sprintf(ptr, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); +#endif + ptr += sprintf(ptr, "Media state = \"%s\"\n", + ((priv->media_connected == MFALSE) ? + "Disconnected" : + "Connected")); + ptr += sprintf(ptr, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? + "on" : + "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < (priv->netdev->num_tx_queues); i++) { + ptr += sprintf( + ptr, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped( + netdev_get_tx_queue( + priv->netdev, i))) ? + "stopped" : + "started")); + } +#else + ptr += sprintf(ptr, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? + "stopped" : + "started")); +#endif + ptr += sprintf(ptr, "%s: num_tx_timeout = %d\n", + priv->netdev->name, + priv->num_tx_timeout); + } + } + + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function save moal_handle's info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_dump_moal_drv_info(moal_handle *phandle, t_u8 *buf) +{ + char *ptr; +#ifdef USB + struct usb_card_rec *cardp = NULL; +#endif + char str_buf[MLAN_MAX_VER_STR_LEN]; + + ENTER(); + if (!phandle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } +#ifdef USB + if (IS_USB(phandle->card_type)) + cardp = (struct usb_card_rec *)phandle->card; +#endif + ptr = (char *)buf; + ptr += sprintf(ptr, "------------moal_debug_info-------------\n"); + woal_get_version(phandle, str_buf, sizeof(str_buf) - 1); + ptr += sprintf(ptr, "Driver version = %s\n", str_buf); + ptr += sprintf(ptr, "main_state = %d\n", phandle->main_state); +#ifdef USB + if (IS_USB(phandle->card_type)) { + ptr += sprintf(ptr, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + ptr += sprintf(ptr, "tx_data_urb_pending = %d\n", + atomic_read(&cardp->tx_data_urb_pending)); +#ifdef USB_CMD_DATA_EP + ptr += sprintf(ptr, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); +#endif + ptr += sprintf(ptr, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } +#endif + ptr += sprintf(ptr, "ioctl_pending = %d\n", + atomic_read(&phandle->ioctl_pending)); + ptr += sprintf(ptr, "tx_pending = %d\n", + atomic_read(&phandle->tx_pending)); + ptr += sprintf(ptr, "rx_pending = %d\n", + atomic_read(&phandle->rx_pending)); + ptr += sprintf(ptr, "lock_count = %d\n", + atomic_read(&phandle->lock_count)); + ptr += sprintf(ptr, "malloc_count = %d\n", + atomic_read(&phandle->malloc_count)); + ptr += sprintf(ptr, "mbufalloc_count = %d\n", + atomic_read(&phandle->mbufalloc_count)); +#ifdef PCIE + if (IS_PCIE(phandle->card_type)) { + ptr += sprintf(ptr, "malloc_cons_count = %d\n", + atomic_read(&phandle->malloc_cons_count)); + } +#endif + ptr += sprintf(ptr, "hs_skip_count = %u\n", phandle->hs_skip_count); + ptr += sprintf(ptr, "hs_force_count = %u\n", phandle->hs_force_count); + + ptr += woal_dump_priv_drv_info(phandle, ptr); + ptr += sprintf(ptr, "------------moal_debug_info End-------------\n"); + + if (phandle->ops.dump_reg_info) + ptr += phandle->ops.dump_reg_info(phandle, ptr); + + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function save mlan's info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_dump_mlan_drv_info(moal_private *priv, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; +#ifdef SDIO + int j; + t_u8 mp_aggr_pkt_limit = 0; +#endif + char str[11 * DBG_CMD_NUM + 1] = {0}; + char *s; + mlan_debug_info *info = NULL; + + ENTER(); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or priv->phandle is null\n"); + LEAVE(); + return 0; + } + info = &(priv->phandle->debug_info); + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return 0; + } + ptr += sprintf(ptr, "------------mlan_debug_info-------------\n"); + ptr += sprintf(ptr, "mlan_processing =%d\n", info->mlan_processing); + ptr += sprintf(ptr, "main_lock_flag =%d\n", info->main_lock_flag); + ptr += sprintf(ptr, "main_process_cnt =%d\n", info->main_process_cnt); + ptr += sprintf(ptr, "delay_task_flag =%d\n", info->delay_task_flag); + ptr += sprintf(ptr, "mlan_rx_processing =%d\n", + info->mlan_rx_processing); + ptr += sprintf(ptr, "rx_pkts_queued =%d\n", info->rx_pkts_queued); + ptr += sprintf(ptr, "tx_pkts_queued =%d\n", info->tx_pkts_queued); + ptr += sprintf(ptr, "fw_hang_report = %d\n", info->fw_hang_report); + ptr += sprintf(ptr, "num_cmd_timeout = %d\n", info->num_cmd_timeout); + ptr += sprintf(ptr, "Timeout cmd id = 0x%x, act = 0x%x\n", + info->timeout_cmd_id, info->timeout_cmd_act); + ptr += sprintf(ptr, "last_cmd_index = %d\n", info->last_cmd_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_id[i]); + ptr += sprintf(ptr, "last_cmd_id = %s\n", str); + + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_act[i]); + + ptr += sprintf(ptr, "last_cmd_act = %s\n", str); + ptr += sprintf(ptr, "last_cmd_resp_index = %d\n", + info->last_cmd_resp_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_resp_id[i]); + + ptr += sprintf(ptr, "last_cmd_resp_id = %s\n", str); + ptr += sprintf(ptr, "last_event_index = %d\n", info->last_event_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_event[i]); + + ptr += sprintf(ptr, "last_event = %s\n", str); + ptr += sprintf(ptr, "num_data_h2c_failure = %d\n", + info->num_tx_host_to_card_failure); + ptr += sprintf(ptr, "num_cmd_h2c_failure = %d\n", + info->num_cmd_host_to_card_failure); + ptr += sprintf(ptr, "num_alloc_buffer_failure = %d\n", + info->num_alloc_buffer_failure); + ptr += sprintf(ptr, "num_pkt_dropped = %d\n", info->num_pkt_dropped); +#ifdef SDIO + if (IS_SD(priv->phandle->card_type)) { + ptr += sprintf(ptr, "num_data_c2h_failure = %d\n", + info->num_rx_card_to_host_failure); + ptr += sprintf(ptr, "num_cmdevt_c2h_failure = %d\n", + info->num_cmdevt_card_to_host_failure); + ptr += sprintf(ptr, "num_int_read_failure = %d\n", + info->num_int_read_failure); + ptr += sprintf(ptr, "last_int_status = %d\n", + info->last_int_status); + ptr += sprintf(ptr, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + (unsigned int)info->mp_rd_bitmap, + info->curr_rd_port); + ptr += sprintf(ptr, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + (unsigned int)info->mp_wr_bitmap, + info->curr_wr_port); + ptr += sprintf(ptr, "mp_invalid_update=%d\n", + info->mp_invalid_update); + mp_aggr_pkt_limit = info->mp_aggr_pkt_limit; + ptr += sprintf(ptr, + "last_recv_wr_bitmap=0x%x last_mp_index = %d\n", + info->last_recv_wr_bitmap, info->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + for (s = str, j = 0; j < mp_aggr_pkt_limit; j++) + s += sprintf( + s, "0x%02x ", + info->last_mp_wr_info + [i * mp_aggr_pkt_limit + j]); + + ptr += sprintf( + ptr, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n%s\n", + info->last_mp_wr_bitmap[i], + info->last_mp_wr_ports[i], + info->last_mp_wr_len[i], + info->last_curr_wr_port[i], str); + } + } +#endif +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + ptr += sprintf(ptr, "txbd_rdptr=0x%x txbd_wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + ptr += sprintf(ptr, "rxbd_rdptr=0x%x rxbd_wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + ptr += sprintf(ptr, "eventbd_rdptr=0x%x event_wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + ptr += sprintf(ptr, "last_wr_index:%d\n", + info->txbd_wrptr & (MLAN_MAX_TXRX_BD - 1)); + ptr += sprintf(ptr, "Tx pkt size:\n"); + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + ptr += sprintf(ptr, "%04d ", info->last_tx_pkt_size[i]); + if (((i + 1) % 16) == 0) + ptr += sprintf(ptr, "\n"); + } + } +#endif + ptr += sprintf(ptr, "num_event_deauth = %d\n", info->num_event_deauth); + ptr += sprintf(ptr, "num_event_disassoc = %d\n", + info->num_event_disassoc); + ptr += sprintf(ptr, "num_event_link_lost = %d\n", + info->num_event_link_lost); + ptr += sprintf(ptr, "num_cmd_deauth = %d\n", info->num_cmd_deauth); + ptr += sprintf(ptr, "num_cmd_assoc_success = %d\n", + info->num_cmd_assoc_success); + ptr += sprintf(ptr, "num_cmd_assoc_failure = %d\n", + info->num_cmd_assoc_failure); + ptr += sprintf(ptr, "num_cons_assoc_failure = %d\n", + info->num_cons_assoc_failure); + ptr += sprintf(ptr, "cmd_resp_received = %d\n", + info->cmd_resp_received); + ptr += sprintf(ptr, "event_received = %d\n", info->event_received); + ptr += sprintf(ptr, "max_tx_buf_size = %d\n", info->max_tx_buf_size); + ptr += sprintf(ptr, "tx_buf_size = %d\n", info->tx_buf_size); + ptr += sprintf(ptr, "curr_tx_buf_size = %d\n", info->curr_tx_buf_size); + + ptr += sprintf(ptr, "data_sent=%d cmd_sent=%d\n", info->data_sent, + info->cmd_sent); + ptr += sprintf(ptr, "ps_mode=%d ps_state=%d\n", info->ps_mode, + info->ps_state); + ptr += sprintf( + ptr, "wakeup_dev_req=%d wakeup_tries=%d pm_wakeup_timeout=%d\n", + info->pm_wakeup_card_req, info->pm_wakeup_fw_try, + info->pm_wakeup_timeout); + ptr += sprintf(ptr, "hs_configured=%d hs_activated=%d\n", + info->is_hs_configured, info->hs_activated); + ptr += sprintf(ptr, "pps_uapsd_mode=%d sleep_pd=%d\n", + info->pps_uapsd_mode, info->sleep_pd); + ptr += sprintf(ptr, "tx_lock_flag = %d\n", info->tx_lock_flag); + ptr += sprintf(ptr, "port_open = %d\n", info->port_open); + ptr += sprintf(ptr, "scan_processing = %d\n", info->scan_processing); + +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + ptr += sprintf(ptr, "txbd: rdptr=0x%x wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + ptr += sprintf(ptr, "rxbd: rdptr=0x%x wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + ptr += sprintf(ptr, "eventbd: rdptr=0x%x wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + ptr += sprintf(ptr, "TXBD Ring:\n"); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->txbd_ring_vbase, + info->txbd_ring_size, MTRUE, ptr); + ptr += sprintf(ptr, "RXBD Ring:\n"); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->rxbd_ring_vbase, + info->rxbd_ring_size, MTRUE, ptr); + ptr += sprintf(ptr, "EVTBD Ring:\n"); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->evtbd_ring_vbase, + info->evtbd_ring_size, MTRUE, ptr); + } +#endif + ptr += sprintf(ptr, "------------mlan_debug_info End-------------\n"); + + LEAVE(); + return ptr - (char *)buf; +} +#define HostCmd_CMD_CFG_DATA 0x008f +#define DEF_FW_PATH "/lib/firmware/" +#define DEF_HOSTCMD_PATH "/lib/firmware/nxp/hostcmd.conf" +/** + * @brief This function save the hostcmd response to file + * + * @param phandle A pointer to moal_handle + * @param pevent A pointer to mlan_cmdresp_event + * + * @return N/A + */ +t_void woal_save_host_cmdresp(moal_handle *phandle, mlan_cmdresp_event *pevent) +{ + HostCmd_DS_GEN *resp; + char file_path[256]; + struct file *pfile = NULL; + char *dpd_data_cfg = phandle->params.dpd_data_cfg; + int ret; + t_u8 *buf; + t_u16 command; + int len = 0; + char *ptr; + loff_t pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + resp = (HostCmd_DS_GEN *)pevent->resp; + command = woal_le16_to_cpu(resp->command); + memset(file_path, 0, sizeof(file_path)); + ret = moal_vmalloc(phandle, pevent->event_len * 5, &buf); + if (ret != MLAN_STATUS_SUCCESS || !buf) { + PRINTM(MERROR, "Fail to allocate memory to save hostcmd\n"); + return; + } + memset(buf, 0, pevent->event_len * 5); + ptr = (char *)buf; + switch (command) { + case HostCmd_CMD_CFG_DATA: + if (dpd_data_cfg) + sprintf(file_path, "%s%s", DEF_FW_PATH, dpd_data_cfg); + else + sprintf(file_path, "%s", DEF_HOSTCMD_PATH); + break; + default: + sprintf(file_path, "%s", DEF_HOSTCMD_PATH); + break; + } + pfile = filp_open(file_path, O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile)) { + PRINTM(MERROR, "Cannot create file %s\n", file_path); + moal_vfree(phandle, buf); + return; + } + ptr += sprintf(ptr, "hostcmd_%02x=={\n", command); + ptr += woal_save_hex_dump(ROW_SIZE_16, resp, pevent->event_len, MFALSE, + ptr); + ptr += sprintf(ptr, "}\n"); + len = ptr - (char *)buf; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, buf, len, &pos); + set_fs(fs); +#else + kernel_write(pfile, buf, len, &pos); +#endif + PRINTM(MMSG, "Save hostcmd 0x%02x, cmd len=%d file len=%d to file %s\n", + command, pevent->event_len, len, file_path); + if (buf) + moal_vfree(phandle, buf); + filp_close(pfile, NULL); + return; +} + +/** + * @brief This function dump moal hex to file + * + * @param phandle A pointer to moal_handle + * @param buf A pointer to buffer + * + * @return The length of this log + */ +static int woal_dump_moal_hex(moal_handle *phandle, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; + ENTER(); + + if (!phandle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + ptr += sprintf(ptr, "<--moal_handle-->\n"); + ptr += sprintf(ptr, "moal_handle=%p, size=%ld(0x%lx)\n", phandle, + (long int)sizeof(*phandle), + (long unsigned int)sizeof(*phandle)); + ptr += woal_save_hex_dump(ROW_SIZE_16, phandle, sizeof(*phandle), MTRUE, + ptr); + ptr += sprintf(ptr, "<--moal_handle End-->\n"); + + for (i = 0; i < phandle->priv_num; i++) { + ptr += sprintf(ptr, "<--moal_private(%d)-->\n", i); + ptr += sprintf(ptr, "moal_private=%p, size=%ld(0x%lx)\n", + phandle->priv[i], + (long int)sizeof(*(phandle->priv[i])), + (long unsigned int)sizeof(*(phandle->priv[i]))); + ptr += woal_save_hex_dump(ROW_SIZE_16, phandle->priv[i], + sizeof(*(phandle->priv[i])), MTRUE, + ptr); + ptr += sprintf(ptr, "<--moal_private(%d) End-->\n", i); + } + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function dump mlan hex to file + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to buffer + * + * @return The length of this log + */ +static int woal_dump_mlan_hex(moal_private *priv, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; + mlan_debug_info *info = NULL; + + ENTER(); + + if (!buf || !priv || !priv->phandle) { + PRINTM(MERROR, "buf priv or priv->phandle is null\n"); + LEAVE(); + return 0; + } + info = &(priv->phandle->debug_info); + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + ptr += sprintf(ptr, "<--mlan_adapter-->\n"); + ptr += sprintf(ptr, "mlan_adapter=%p, size=%d(0x%x)\n", + info->mlan_adapter, info->mlan_adapter_size, + info->mlan_adapter_size); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->mlan_adapter, + info->mlan_adapter_size, MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_adapter End-->\n"); +#ifdef SDIO + if (IS_SD(priv->phandle->card_type) && info->mpa_buf && + info->mpa_buf_size) { + ptr += sprintf(ptr, "<--mlan_mpa_buf-->\n"); + ptr += sprintf(ptr, "mlan_mpa_buf=%p, size=%d(0x%x)\n", + info->mpa_buf, info->mpa_buf_size, + info->mpa_buf_size); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->mpa_buf, + info->mpa_buf_size, MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_mpa_buf End-->\n"); + } +#endif + for (i = 0; i < info->mlan_priv_num; i++) { + ptr += sprintf(ptr, "<--mlan_private(%d)-->\n", i); + ptr += sprintf(ptr, "mlan_private=%p, size=%d(0x%x)\n", + info->mlan_priv[i], info->mlan_priv_size[i], + info->mlan_priv_size[i]); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->mlan_priv[i], + info->mlan_priv_size[i], MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_private(%d) End-->\n", i); + } + + LEAVE(); + return ptr - (char *)buf; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief This function create dump directory + * + * @param phandle A pointer to moal_handle + * @param dir_buf A pointer to dir_buf buffer + * @param buf_size Size of dir_buf buffer + * + * @return N/A + */ +void woal_create_dump_dir(moal_handle *phandle, char *dir_buf, int buf_size) +{ + struct dentry *dentry; + struct path path; + t_u32 sec, usec; + int ret; + + ENTER(); + + if (!phandle || !dir_buf) { + PRINTM(MERROR, "Can't create directory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + moal_get_system_time(phandle, &sec, &usec); + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s%u", "/data/dump_", sec); + + dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1); + if (IS_ERR(dentry)) { + PRINTM(MERROR, + "Create directory %s error, try create dir in /var", + dir_buf); + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s%u", "/var/dump_", sec); + dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1); + } + if (IS_ERR(dentry)) { + PRINTM(MERROR, "Create directory %s error, use default folder", + dir_buf); + goto default_dir; + } + ret = vfs_mkdir(path.dentry->d_inode, dentry, 0777); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + mutex_unlock(&path.dentry->d_inode->i_mutex); +#else + inode_unlock(path.dentry->d_inode); +#endif + + if (ret < 0) { + PRINTM(MERROR, + "Create directory failure, use default folder\n"); + PRINTM(MERROR, "Create directory failure, ret = %d\n", ret); + goto default_dir; + } else { + PRINTM(MMSG, "Create directory %s successfully\n", dir_buf); + goto done; + } + +default_dir: + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s", "/data"); +done: + LEAVE(); +} +#endif + +/** + * @brief This function save dump buf to file + * + * @param dir_name A pointer to directory name + * @param file_name A pointer to file name + * @param buf A pointer to dump data + * @param buf_len The length of dump buf + * + * @return SUCCESS OR FAILURE + */ +mlan_status woal_save_dump_info_to_file(char *dir_name, char *file_name, + t_u8 *buf, t_u32 buf_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct file *pfile = NULL; + t_u8 name[64]; + loff_t pos; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(MERROR, "Can't save dump info to file\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + sprintf(name, "%s/%s", dir_name, file_name); + pfile = filp_open(name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MMSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + sprintf(name, "%s/%s", "/var", file_name); + pfile = filp_open(name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(MERROR, "Create Dump file for %s error\n", name); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MMSG, "Dump data %s saved in %s\n", file_name, name); + + pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, buf, buf_len, &pos); + set_fs(fs); +#else + kernel_write(pfile, buf, buf_len, &pos); +#endif + filp_close(pfile, NULL); + + PRINTM(MMSG, "Dump data %s saved in %s successfully\n", file_name, + name); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function dump drv info to file + * + * @param phandle A pointer to moal_handle + * @param dir_name A pointer to directory name + * + * @return N/A + */ +void woal_dump_drv_info(moal_handle *phandle, t_u8 *dir_name) +{ + int ret = 0; + struct file *pfile = NULL; + t_u8 *drv_buf = NULL; + t_u8 file_name[64]; + t_u32 len = 0; + t_u32 total_len = 0; + moal_private *woal_handle = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + t_u32 drv_info_size = DRV_INFO_SIZE; + + ENTER(); + + PRINTM(MMSG, "=== START DRIVER INFO DUMP==="); + memset(file_name, 0, sizeof(file_name)); + if (phandle->second_mac) + sprintf(file_name, "%s/%s", dir_name, "file_drv_info_2"); + else + sprintf(file_name, "%s/%s", dir_name, "file_drv_info"); + pfile = filp_open(file_name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MMSG, + "Create file %s error, try create /var/file_drv_info", + file_name); + pfile = filp_open("/var/file_drv_info", O_CREAT | O_RDWR, 0644); + } else { + PRINTM(MMSG, "DRV dump data in %s\n", file_name); + } + if (IS_ERR(pfile)) { + PRINTM(MMSG, "Create file_drv_info file failed\n"); + goto done; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); +#endif + woal_handle = woal_get_priv(phandle, MLAN_BSS_ROLE_ANY); + + if (woal_handle != NULL) { + if (phandle->priv_num > 3) + drv_info_size += + (phandle->priv_num - 3) * DRV_INFO_PER_INTF; + ret = moal_vmalloc(phandle, drv_info_size, &drv_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !drv_buf) { + PRINTM(MERROR, "Error: vmalloc drv buffer failed!\n"); + goto done; + } + len = woal_dump_moal_drv_info(phandle, drv_buf); + total_len += len; + len = woal_dump_mlan_drv_info(woal_handle, drv_buf + total_len); + total_len += len; + len = woal_dump_moal_hex(phandle, drv_buf + total_len); + total_len += len; + len = woal_dump_mlan_hex(woal_handle, drv_buf + total_len); + total_len += len; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile, drv_buf, total_len, &pfile->f_pos); +#else + kernel_write(pfile, drv_buf, total_len, &pfile->f_pos); +#endif + } + PRINTM(MMSG, "Drv info total bytes = %ld (0x%lx)\n", + (long int)total_len, (long unsigned int)total_len); + filp_close(pfile, NULL); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + set_fs(fs); +#endif + PRINTM(MMSG, "=== DRIVER INFO DUMP END==="); +done: + if (drv_buf) + moal_vfree(phandle, drv_buf); + LEAVE(); +} + +/** + * @brief This function displays extra MOAL debug information + * + * @param priv A pointer to moal_private + * @param handle A pointer to moal_handle + * @param flag Indicates whether register read can be done directly + * + * @return N/A + */ +void woal_moal_debug_info(moal_private *priv, moal_handle *handle, u8 flag) +{ + moal_handle *phandle = NULL; +#ifdef USB + struct usb_card_rec *cardp = NULL; +#endif + char buf[MLAN_MAX_VER_STR_LEN]; + int i = 0; + + ENTER(); + + if (!priv) { + if (handle) { + phandle = handle; + } else { + PRINTM(MERROR, + "Could not retrieve debug information from MOAL\n"); + LEAVE(); + return; + } + } else { + phandle = priv->phandle; + } +#ifdef USB + if (IS_USB(phandle->card_type)) { + cardp = (struct usb_card_rec *)phandle->card; + PRINTM(MERROR, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + PRINTM(MERROR, "tx_data_urb_pending = %d\n", + atomic_read(&cardp->tx_data_urb_pending)); +#ifdef USB_CMD_DATA_EP + PRINTM(MERROR, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); +#endif + PRINTM(MERROR, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } +#endif + + woal_get_version(phandle, buf, sizeof(buf) - 1); + PRINTM(MERROR, "Driver version = %s\n", buf); + PRINTM(MERROR, "main_state = %d\n", phandle->main_state); + PRINTM(MERROR, "ioctl_pending = %d\n", + atomic_read(&phandle->ioctl_pending)); + PRINTM(MERROR, "tx_pending = %d\n", atomic_read(&phandle->tx_pending)); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + if (priv) { + PRINTM(MERROR, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + PRINTM(MERROR, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + PRINTM(MERROR, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + PRINTM(MERROR, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + } +#endif + PRINTM(MERROR, "rx_pending = %d\n", atomic_read(&phandle->rx_pending)); + PRINTM(MERROR, "lock_count = %d\n", atomic_read(&phandle->lock_count)); + PRINTM(MERROR, "malloc_count = %d\n", + atomic_read(&phandle->malloc_count)); + PRINTM(MERROR, "mbufalloc_count = %d\n", + atomic_read(&phandle->mbufalloc_count)); +#ifdef PCIE + if (IS_PCIE(phandle->card_type)) { + PRINTM(MERROR, "malloc_cons_count = %d\n", + atomic_read(&phandle->malloc_cons_count)); + } +#endif + PRINTM(MERROR, "hs_skip_count = %u\n", phandle->hs_skip_count); + PRINTM(MERROR, "hs_force_count = %u\n", phandle->hs_force_count); + + if (priv && priv->netdev) { + PRINTM(MERROR, "Media state = \"%s\"\n", + ((priv->media_connected == MFALSE) ? "Disconnected" : + "Connected")); + PRINTM(MERROR, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < (priv->netdev->num_tx_queues); i++) { + PRINTM(MERROR, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped( + netdev_get_tx_queue(priv->netdev, i))) ? + "stopped" : + "started")); + } +#else + PRINTM(MERROR, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : + "started")); +#endif + } + + for (i = 0; i < phandle->priv_num; i++) { + priv = phandle->priv[i]; + if (priv && priv->netdev) + PRINTM(MERROR, "%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (phandle->is_suspended == MTRUE) { + LEAVE(); + return; + } + +#ifdef PCIE + if (IS_PCIE(phandle->card_type)) { + if (phandle->ops.reg_dbg && (drvdbg & (MREG_D | MFW_D))) + phandle->ops.reg_dbg(phandle); + } +#endif +#ifdef SDIO + if (IS_SD(phandle->card_type)) { + if (flag && ((phandle->main_state == MOAL_END_MAIN_PROCESS) || + (phandle->main_state == MOAL_STATE_IDLE))) { + if (phandle->ops.reg_dbg && (drvdbg & (MREG_D | MFW_D))) + phandle->ops.reg_dbg(phandle); + } else { + if (drvdbg & (MREG_D | MFW_D)) { + phandle->reg_dbg = MTRUE; + queue_work(phandle->workqueue, + &phandle->main_work); + } + } + } +#endif +#ifdef DEBUG_LEVEL1 + if (drvdbg & MFW_D) { + drvdbg &= ~MFW_D; + phandle->fw_dbg = MTRUE; + queue_work(phandle->workqueue, &phandle->main_work); + } +#endif + LEAVE(); + return; +} + +/** + * @brief Download power table to firmware for a specific country + * + * @param priv A pointer to moal_private + * @param country ISO 3166-1 alpha-2 country code + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_request_country_power_table(moal_private *priv, char *country) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = NULL; + char country_name[] = "txpower_XX.bin"; + char file_path[256]; + char *last_slash = NULL; + char *fw_name = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "Priv or handle is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (!country) { + PRINTM(MERROR, "Country is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (fw_region) + strncpy(country_name, "rgpower_XX.bin", + strlen("rgpower_XX.bin")); +#endif +#endif + handle = priv->phandle; + + /* Replace XX with ISO 3166-1 alpha-2 country code */ + strncpy(strstr(country_name, "XX"), country, strlen(country)); + fw_name = handle->params.fw_name; + memset(file_path, 0, sizeof(file_path)); + /* file_path should be Null terminated */ + if (fw_name) { + strncpy(file_path, fw_name, sizeof(file_path) - 1); + last_slash = strrchr(file_path, '/'); + if (last_slash) + memset(last_slash + 1, 0, + sizeof(file_path) - 1 - + (last_slash - file_path)); + else + memset(file_path, 0, sizeof(file_path)); + } else { + strncpy(file_path, "nxp/", + MIN((sizeof(file_path) - 1), strlen("nxp/"))); + } + + if ((strlen(file_path) + strlen(country_name)) < + (sizeof(file_path) - 1)) + strncpy(file_path + strlen(file_path), country_name, + sizeof(file_path) - strlen(file_path)); + else { + PRINTM(MERROR, + "file path buffer too small, fail to dnld power table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MMSG, "Trying download country_power_tble: %s\n", file_path); + ret = woal_set_user_init_data(handle, COUNTRY_POWER_TABLE, + MOAL_IOCTL_WAIT, file_path); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + /* Try download WW rgpowertable */ + if (fw_region && (ret == MLAN_STATUS_FILE_ERR)) { + strncpy(country_name, "rgpower_WW.bin", + strlen("rgpower_WW.bin")); + last_slash = strrchr(file_path, '/'); + if (last_slash) + memset(last_slash + 1, 0, + sizeof(file_path) - 1 - + (last_slash - file_path)); + else + memset(file_path, 0, sizeof(file_path)); + strncpy(file_path + strlen(file_path), country_name, + strlen(country_name)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + handle->country_code[0] = '0'; + handle->country_code[1] = '0'; +#endif + PRINTM(MMSG, "Trying again download country_power_tble: %s\n", + file_path); + ret = woal_set_user_init_data(handle, COUNTRY_POWER_TABLE, + MOAL_IOCTL_WAIT, file_path); + } +#endif +#endif + LEAVE(); + return ret; +} + +/** + * @brief napi polling call back function. + * + * @param napi A pointer to napi_struct + * @param budget the limit of packets driver should poll + * + * @return packets received + */ +int woal_netdev_poll_rx(struct napi_struct *napi, int budget) +{ + moal_handle *handle = container_of(napi, moal_handle, napi_rx); + t_u8 recv = budget; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + napi_complete(napi); + LEAVE(); + return 0; + } + mlan_rx_process(handle->pmlan_adapter, &recv); + if (recv < budget) + napi_complete(napi); + LEAVE(); + return recv; +} + +/** + * @brief This workqueue function handles woal event queue + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_evt_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, evt_work); + struct woal_event *evt; + unsigned long flags; +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + moal_private *priv; +#endif +#endif + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + spin_lock_irqsave(&handle->evt_lock, flags); + while (!list_empty(&handle->evt_queue)) { + evt = list_first_entry(&handle->evt_queue, struct woal_event, + link); + list_del(&evt->link); + spin_unlock_irqrestore(&handle->evt_lock, flags); + switch (evt->type) { + case WOAL_EVENT_CHAN_SWITCH: +#if defined(UAP_SUPPORT) || defined(STA_SUPPORT) +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + woal_cfg80211_notify_channel((moal_private *)evt->priv, + &evt->chan_info); +#endif +#endif +#endif + break; + case WOAL_EVENT_BGSCAN_STOP: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + woal_cfg80211_notify_sched_scan_stop( + (moal_private *)evt->priv); +#endif +#endif + break; +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + case WOAL_EVENT_DEAUTH: + priv = evt->priv; + woal_host_mlme_disconnect(evt->priv, evt->reason_code, + priv->cfg_bssid); + break; + + case WOAL_EVENT_ASSOC_RESP: + woal_host_mlme_process_assoc_resp( + (moal_private *)evt->priv, &evt->assoc_resp); + break; +#endif +#endif + } + kfree(evt); + spin_lock_irqsave(&handle->evt_lock, flags); + } + spin_unlock_irqrestore(&handle->evt_lock, flags); + LEAVE(); +} +/** + * @brief This workqueue function handles rx_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_rx_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, rx_work); +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 14, 6) + moal_private *priv; +#endif +#endif +#endif + wifi_timeval start_timeval; + wifi_timeval end_timeval; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + + woal_get_monotonic_time(&start_timeval); + mlan_rx_process(handle->pmlan_adapter, NULL); + + woal_get_monotonic_time(&end_timeval); + handle->rx_time += (t_u64)(timeval_to_usec(end_timeval) - + timeval_to_usec(start_timeval)); + PRINTM(MINFO, + "%s : start_timeval=%d:%d end_timeval=%d:%d inter=%llu rx_time=%llu\n", + __func__, start_timeval.time_sec, start_timeval.time_usec, + end_timeval.time_sec, end_timeval.time_usec, + (t_u64)(timeval_to_usec(end_timeval) - + timeval_to_usec(start_timeval)), + handle->rx_time); + LEAVE(); +} + +/** + * @brief This function dequeue pkt from list + * + * @param list A pointer to struct sk_buff_head + * + * @return skb buffer + */ + +struct sk_buff *woal_skb_dequeue_spinlock(struct sk_buff_head *list) +{ + struct sk_buff *result; + + spin_lock_bh(&list->lock); + result = __skb_dequeue(list); + spin_unlock_bh(&list->lock); + return result; +} + +/** + * @brief This workqueue function handles rx_work_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_tx_work_handler(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, tx_work); + moal_private *priv = NULL; + int i = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + priv = handle->priv[i]; + while ((skb = woal_skb_dequeue_spinlock(&priv->tx_q)) != NULL) { + woal_start_xmit(priv, skb); + } + } + + LEAVE(); +} + +/** + * @brief This workqueue function handles main_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_main_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, main_work); +#ifdef USB + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + struct sched_param sp = {.sched_priority = wq_sched_prio}; +#endif + + ENTER(); + + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + if (handle->reg_dbg == MTRUE) { + handle->reg_dbg = MFALSE; + if (handle->ops.reg_dbg) + handle->ops.reg_dbg(handle); + } + if (handle->fw_dbg == MTRUE) { + handle->fw_dbg = MFALSE; + handle->ops.dump_fw_info(handle); + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + /* Change the priority and scheduling policy of main work queue + */ + if ((handle->params.wq_sched_prio != current->rt_priority) || + (handle->params.wq_sched_policy != current->policy)) { + PRINTM(MMSG, + "Set work queue priority %d and scheduling policy %d\n", + handle->params.wq_sched_prio, + handle->params.wq_sched_policy); + sched_setscheduler(current, handle->params.wq_sched_policy, + &sp); + } +#endif + + handle->main_state = MOAL_ENTER_WORK_QUEUE; +#ifdef USB + /* WAR for no free skb issue */ + if (IS_USB(handle->card_type) && !atomic_read(&handle->rx_pending) && + atomic_read(&cardp->rx_data_urb_pending) < MVUSB_RX_DATA_URB) { + PRINTM(MWARN, "Try to resubmit Rx data URBs\n"); + woal_usb_submit_rx_data_urbs(handle); + } +#endif + handle->main_state = MOAL_START_MAIN_PROCESS; + /* Call MLAN main process */ + mlan_main_process(handle->pmlan_adapter); + handle->main_state = MOAL_END_MAIN_PROCESS; + + LEAVE(); +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the mlan_private and initialize the device. + * + * @param card A pointer to card + * @param dev A pointer to device structure + * @param if_ops A pointer to interface ops + * @param card_type card type id + * + * @return A pointer to moal_handle structure + */ +moal_handle *woal_add_card(void *card, struct device *dev, moal_if_ops *if_ops, + t_u16 card_type) +{ + moal_handle *handle = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int netlink_num = NETLINK_NXP; + int index = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct netlink_kernel_cfg cfg = { + .groups = NL_MULTICAST_GROUP, + }; +#endif + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == NULL) + break; + } + if (index >= MAX_MLAN_ADAPTER) + goto err_handle; + + /* Allocate buffer for moal_handle */ + handle = kzalloc(sizeof(moal_handle), GFP_KERNEL); + if (!handle) { + PRINTM(MERROR, "Allocate buffer for moal_handle failed!\n"); + goto err_handle; + } + + /* Init moal_handle */ + handle->card = card; + + /* Save the handle */ + m_handle[index] = handle; + handle->handle_idx = index; + + handle->hotplug_device = dev; + handle->card_type = card_type; + /* Attach moal handle ops */ + PRINTM(MMSG, "Attach moal handle ops, card interface type: 0x%x\n", + handle->card_type); + moal_memcpy_ext(handle, &handle->ops, if_ops, sizeof(*if_ops), + sizeof(handle->ops)); + handle->second_mac = handle->ops.is_second_mac(handle); + if (handle->second_mac) { + if ((index >= 1) && m_handle[index - 1]) { + handle->pref_mac = (void *)m_handle[index - 1]; + m_handle[index - 1]->pref_mac = (void *)handle; + } + } + + /* Init module parameters */ + woal_init_module_param(handle); + + if (handle->params.mac_addr +#ifdef MFG_CMD_SUPPORT + && handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED +#endif + ) { + t_u8 temp[20]; + t_u8 len = strlen(handle->params.mac_addr) + 1; + if (len < sizeof(temp)) { + moal_memcpy_ext(handle, temp, handle->params.mac_addr, + len, sizeof(temp)); + handle->set_mac_addr = 1; + /* note: the following function overwrites the + * temp buffer */ + woal_mac2u8(handle->mac_addr, temp); + } + } + + /* Get card info */ + woal_get_card_info(handle); + /** Get card revision */ + handle->ops.get_fw_name(handle); +#ifdef STA_SUPPORT + handle->scan_pending_on_block = MFALSE; + MOAL_INIT_SEMAPHORE(&handle->async_sem); +#endif + +#if defined(USB) + if (IS_USB(handle->card_type)) + handle->boot_state = ((struct usb_card_rec *)card)->boot_state; +#endif /* USB_NEW_FW_DNLD */ + /* Init SW */ + if (MLAN_STATUS_SUCCESS != woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto err_kmalloc; + } + + do { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + handle->nl_sk = netlink_kernel_create(netlink_num, NULL); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + handle->nl_sk = netlink_kernel_create( + netlink_num, NL_MULTICAST_GROUP, NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + handle->nl_sk = + netlink_kernel_create(netlink_num, NL_MULTICAST_GROUP, + NULL, NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + handle->nl_sk = netlink_kernel_create(&init_net, netlink_num, + NL_MULTICAST_GROUP, NULL, + NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + handle->nl_sk = netlink_kernel_create(&init_net, netlink_num, + THIS_MODULE, &cfg); +#else + handle->nl_sk = + netlink_kernel_create(&init_net, netlink_num, &cfg); +#endif +#endif +#endif +#endif +#endif + if (handle->nl_sk) { + PRINTM(MINFO, "Netlink number = %d\n", netlink_num); + handle->netlink_num = netlink_num; + break; + } + netlink_num--; + } while (netlink_num > 0); + + if (handle->nl_sk == NULL) { + PRINTM(MERROR, + "Could not initialize netlink event passing mechanism!\n"); + goto err_kmalloc; + } + + /* Create workqueue for main process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->workqueue = create_workqueue("MOAL_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->workqueue = alloc_workqueue( + "MOAL_WORK_QUEUE", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->workqueue = create_workqueue("MOAL_WORK_QUEUE"); +#endif +#endif + if (!handle->workqueue) + goto err_kmalloc; + + MLAN_INIT_WORK(&handle->main_work, woal_main_work_queue); + + /* Create workqueue for event process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->evt_workqueue = create_workqueue("MOAL_EVT_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->evt_workqueue = + alloc_workqueue("MOAL_EVT_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->evt_workqueue = create_workqueue("MOAL_EVT_WORK_QUEUE"); +#endif +#endif + if (!handle->evt_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + + MLAN_INIT_WORK(&handle->evt_work, woal_evt_work_queue); + INIT_LIST_HEAD(&handle->evt_queue); + spin_lock_init(&handle->evt_lock); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + MLAN_INIT_WORK(&handle->host_mlme_work, woal_host_mlme_work_queue); +#endif +#endif + + if (!moal_extflg_isset(handle, EXT_NAPI)) { + /* Create workqueue for rx process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->rx_workqueue = create_workqueue("MOAL_RX_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->rx_workqueue = alloc_workqueue( + "MOAL_RX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->rx_workqueue = create_workqueue("MOAL_RX_WORK_QUEUE"); +#endif +#endif + if (!handle->rx_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + MLAN_INIT_WORK(&handle->rx_work, woal_rx_work_queue); + } +#define NAPI_BUDGET 64 + if (moal_extflg_isset(handle, EXT_NAPI)) { + init_dummy_netdev(&handle->napi_dev); + netif_napi_add(&handle->napi_dev, &handle->napi_rx, + woal_netdev_poll_rx, NAPI_BUDGET); + napi_enable(&handle->napi_rx); + } + + if (moal_extflg_isset(handle, EXT_TX_WORK)) { + /* Create workqueue for tx process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + handle->tx_workqueue = create_workqueue("MOAL_TX_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->tx_workqueue = alloc_workqueue( + "MOAL_TX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->tx_workqueue = create_workqueue("MOAL_TX_WORK_QUEUE"); +#endif +#endif + if (!handle->tx_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + MLAN_INIT_WORK(&handle->tx_work, woal_tx_work_handler); + } + +#ifdef REASSOCIATION + PRINTM(MINFO, "Starting re-association thread...\n"); + handle->reassoc_thread.handle = handle; + woal_create_thread(woal_reassociation_thread, &handle->reassoc_thread, + "woal_reassoc_service"); + + while (!handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ + + /* Register the device. Fill up the private data structure with + * relevant information from the card and request for the + * required IRQ. + */ + if (handle->ops.register_dev(handle) != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, "Failed to register wlan device!\n"); + goto err_registerdev; + } + woal_update_firmware_name(handle); +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_init(&handle->ws, "mwlan"); +#else + wake_lock_init(&handle->wake_lock, WAKE_LOCK_SUSPEND, "mwlan"); +#endif +#endif + + /* Init FW and HW */ + if (MLAN_STATUS_SUCCESS != woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + goto err_init_fw; + } + +#ifdef SD8887 + if (IS_SD8887(handle->card_type)) { + union { + t_u32 l; + t_u8 c[4]; + } ver; + ver.l = handle->fw_release_number; + if (ver.c[1] == 75) { + handle->card_info->embedded_supp = 0; + PRINTM(MMSG, + "Disable EMBEDED Supplicant for SD8887-FP75\n"); + } + } +#endif + LEAVE(); + return handle; + +err_init_fw: + if ((handle->hardware_status == HardwareStatusFwReady) || + (handle->hardware_status == HardwareStatusReady)) { + PRINTM(MINFO, "shutdown mlan\n"); + handle->init_wait_q_woken = MFALSE; + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + } +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + handle->ops.unregister_dev(handle); +err_registerdev: + handle->surprise_removed = MTRUE; +#ifdef REASSOCIATION + if (handle->reassoc_thread.pid) + wake_up_interruptible(&handle->reassoc_thread.wait_q); + /* waiting for main thread quit */ + while (handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ + if (moal_extflg_isset(handle, EXT_NAPI)) + netif_napi_del(&handle->napi_rx); + woal_terminate_workqueue(handle); +err_kmalloc: + woal_free_moal_handle(handle); + if (index < MAX_MLAN_ADAPTER) + m_handle[index] = NULL; +err_handle: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return NULL; +} + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status woal_remove_card(void *card) +{ + moal_handle *handle = NULL; + moal_private *priv = NULL; + mlan_status status; + int i; + int index = 0; + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + /* Find the correct handle */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] && (m_handle[index]->card == card)) { + handle = m_handle[index]; + break; + } + } + if (!handle) + goto exit_remove; +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode == MLAN_INIT_PARA_ENABLED +#if defined(USB) + && handle->boot_state == USB_FW_READY +#endif + ) { + if (handle->params.fw_name) { + kfree(handle->params.fw_name); + handle->params.fw_name = NULL; + } + } +#endif + if (handle->rf_test_mode) + woal_process_rf_test_mode(handle, MFG_CMD_UNSET_TEST_MODE); + handle->surprise_removed = MTRUE; + + flush_workqueue(handle->workqueue); + flush_workqueue(handle->evt_workqueue); + if (handle->rx_workqueue) + flush_workqueue(handle->rx_workqueue); + if (handle->tx_workqueue) + flush_workqueue(handle->tx_workqueue); + + if (moal_extflg_isset(handle, EXT_NAPI)) { + napi_disable(&handle->napi_rx); + netif_napi_del(&handle->napi_rx); + } + + /* Stop data */ + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->netdev) { + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + } + } + if ((handle->hardware_status == HardwareStatusFwReady) || + (handle->hardware_status == HardwareStatusReady)) { + /* Shutdown firmware */ + PRINTM(MIOCTL, "mlan_shutdown_fw.....\n"); + handle->init_wait_q_woken = MFALSE; + + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + PRINTM(MIOCTL, "mlan_shutdown_fw done!\n"); + } + /* Unregister mlan */ + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + handle->pmlan_adapter = NULL; + } + if (atomic_read(&handle->rx_pending) || + atomic_read(&handle->tx_pending) || + atomic_read(&handle->ioctl_pending)) { + PRINTM(MERROR, + "ERR: rx_pending=%d,tx_pending=%d,ioctl_pending=%d\n", + atomic_read(&handle->rx_pending), + atomic_read(&handle->tx_pending), + atomic_read(&handle->ioctl_pending)); + } + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } +#endif +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + if (handle->is_go_timer_set) { + woal_cancel_timer(&handle->go_timer); + handle->is_go_timer_set = MFALSE; + } +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /* Remove virtual interface */ + woal_remove_virtual_interface(handle); +#endif +#endif +#endif + if (handle->tp_acnt.on) { + handle->tp_acnt.on = 0; + handle->tp_acnt.drop_point = 0; + if (handle->is_tp_acnt_timer_set) { + woal_cancel_timer(&handle->tp_acnt.timer); + handle->is_tp_acnt_timer_set = MFALSE; + } + } + + /* Remove interface */ + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + + woal_terminate_workqueue(handle); + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (handle->is_cac_timer_set) { + woal_cancel_timer(&handle->cac_timer); + handle->is_cac_timer_set = MFALSE; + } +#endif +#endif +#ifdef REASSOCIATION + PRINTM(MINFO, "Free reassoc_timer\n"); + if (handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + if (handle->reassoc_thread.pid) + wake_up_interruptible(&handle->reassoc_thread.wait_q); + + /* waiting for main thread quit */ + while (handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + handle->ops.unregister_dev(handle); +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif + /* Free adapter structure */ + PRINTM(MINFO, "Free Adapter\n"); + woal_free_moal_handle(handle); + + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == handle) { + m_handle[index] = NULL; + break; + } + } +exit_remove: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef CONFIG_PROC_FS +/** + * @brief This function switch the drv_mode + * + * @param handle A pointer to moal_handle structure + * @param mode new drv_mode to switch. + * + * @return MLAN_STATUS_SUCCESS /MLAN_STATUS_FAILURE + * /MLAN_STATUS_PENDING + */ +mlan_status woal_switch_drv_mode(moal_handle *handle, t_u32 mode) +{ + unsigned int i; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + if (woal_update_drv_tbl(handle, mode) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table!\n"); + status = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Reset all interfaces */ + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); + + status = woal_shutdown_fw(priv, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "func shutdown failed!\n"); + goto exit; + } +#ifdef USB + handle->skip_fw_dnld = MTRUE; +#endif + + /* Shutdown firmware */ + PRINTM(MIOCTL, "mlan_shutdown_fw.....\n"); + handle->init_wait_q_woken = MFALSE; + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + PRINTM(MIOCTL, "mlan_shutdown_fw done!\n"); + /* Unregister mlan */ + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + handle->pmlan_adapter = NULL; + } + if (atomic_read(&handle->rx_pending) || + atomic_read(&handle->tx_pending) || + atomic_read(&handle->ioctl_pending)) { + PRINTM(MERROR, + "ERR: rx_pending=%d,tx_pending=%d,ioctl_pending=%d\n", + atomic_read(&handle->rx_pending), + atomic_read(&handle->tx_pending), + atomic_read(&handle->ioctl_pending)); + } + + /* Remove interface */ + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + + if (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d, malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } +#ifdef PCIE + if (IS_PCIE(handle->card_type) && + atomic_read(&handle->malloc_cons_count)) { + PRINTM(MERROR, "mlan has memory leak: malloc_cons_count=%d\n", + atomic_read(&handle->malloc_cons_count)); + } +#endif + + handle->priv_num = 0; + handle->params.drv_mode = mode; + /* Init SW */ + if (woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto exit; + } + /* Init FW and HW */ + if (woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + goto exit; + } + LEAVE(); + return status; +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return status; +} +#endif + +#ifdef SDIO_MMC +#define FW_POLL_TRIES 100 + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * + * @return 0--success, otherwise failure + */ +static int woal_reset_and_reload_fw(moal_handle *handle) +{ + int ret = 0, tries = 0; + t_u32 value = 1; + t_u32 reset_reg = handle->card_info->fw_reset_reg; + t_u8 reset_val = handle->card_info->fw_reset_val; + + ENTER(); + + if (!IS_SD9098(handle->card_type) && !IS_SD9097(handle->card_type)) { + mlan_pm_wakeup_card(handle->pmlan_adapter, MTRUE); + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = handle->ops.write_reg(handle, reset_reg, 0xba); + if (ret == MLAN_STATUS_SUCCESS) { + handle->ops.read_reg(handle, reset_reg, &value); + if (value == 0xba) { + PRINTM(MMSG, "FW wake up\n"); + break; + } + } + udelay(1000); + } + } + /* Write register to notify FW */ + if (handle->ops.write_reg(handle, reset_reg, reset_val) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to write register.\n"); + ret = -EFAULT; + goto done; + } +#if defined(SD9098) || defined(SD9097) + if (IS_SD9098(handle->card_type) || IS_SD9097(handle->card_type)) + handle->ops.write_reg(handle, 0x00, 0x10); +#endif + /* Poll register around 100 ms */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + handle->ops.read_reg(handle, reset_reg, &value); + if (value == 0) + /* FW is ready */ + break; + udelay(1000); + } + + if (value) { + PRINTM(MERROR, "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + if (!IS_SD9098(handle->card_type) && !IS_SD9097(handle->card_type)) + mlan_pm_wakeup_card(handle->pmlan_adapter, MFALSE); + /* Download FW */ + ret = woal_request_fw(handle); + if (ret) { + ret = -EFAULT; + goto done; + } + PRINTM(MMSG, "FW Reload successfully."); +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * + * @return 0--success, otherwise failure + */ +static int woal_reload_fw(moal_handle *handle) +{ + int ret = 0; + ENTER(); + /* Download FW */ + ret = woal_request_fw(handle); + if (ret) { + ret = -EFAULT; + goto done; + } + PRINTM(MMSG, "FW Reload successfully."); +done: + LEAVE(); + return ret; +} + +void woal_pre_reset(moal_handle *handle) +{ + int intf_num; + ENTER(); + /** detach network interface */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_stop_queue(handle->priv[intf_num]->netdev); + netif_device_detach(handle->priv[intf_num]->netdev); + } + handle->fw_reload = MTRUE; + woal_update_firmware_name(handle); +#ifdef USB + if (IS_USB(handle->card_type)) + woal_kill_urbs(handle); +#endif + LEAVE(); +} + +void woal_post_reset(moal_handle *handle) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int intf_num; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT */ + + ENTER(); + /** un-block IOCTL */ + handle->fw_reload = MFALSE; + handle->driver_status = MFALSE; + /* Restart the firmware */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_WARM_RESET; + misc->param.fw_reload = MTRUE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + req, MOAL_IOCTL_WAIT)) { + kfree(req); + goto done; + } + kfree(req); + } + handle->hardware_status = HardwareStatusReady; + /* Reset all interfaces */ + woal_reset_intf(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT, MTRUE); + /* Initialize private structures */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if ((handle->priv[intf_num]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) && + (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP)) { + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &bss_role)) { + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT */ + } + + /* Enable interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + netif_device_attach(handle->priv[intf_num]->netdev); + woal_start_queue(handle->priv[intf_num]->netdev); + } +done: + LEAVE(); + return; +} + +/** + * @brief This function reload fw + * + * @param phandle A pointer to moal_handle structure + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +void woal_request_fw_reload(moal_handle *phandle, t_u8 mode) +{ + int ret = 0; + +#ifdef PCIE + ppcie_service_card card = NULL; + struct pci_dev *pdev = NULL; +#endif + moal_handle *handle = phandle; + moal_handle *ref_handle = NULL; + + ENTER(); +#ifdef PCIE + if (mode == FW_RELOAD_PCIE_RESET) { + card = (pcie_service_card *)handle->card; + pdev = card->dev; + pci_reset_function(pdev); + LEAVE(); + return; + } +#endif + + // handle-> mac0 , ref_handle->second mac + if (handle->pref_mac) { + if (phandle->second_mac) { + handle = (moal_handle *)handle->pref_mac; + ref_handle = phandle; + } else { + ref_handle = (moal_handle *)handle->pref_mac; + } + ref_handle->driver_status = MTRUE; + } + /** start block IOCTL */ + handle->driver_status = MTRUE; + + if (mode == FW_RELOAD_WITH_EMULATION) { + fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MMSG, "FW reload with re-emulation...\n"); + LEAVE(); + return; + } + woal_pre_reset(handle); + if (ref_handle) + woal_pre_reset(ref_handle); + if (mode == FW_RELOAD_NO_EMULATION) { + ret = woal_reload_fw(handle); + if (ref_handle) + woal_reload_fw(ref_handle); + } +#ifdef SDIO_MMC + else if (mode == FW_RELOAD_SDIO_INBAND_RESET && + IS_SD(handle->card_type)) { + ret = woal_reset_and_reload_fw(handle); + if (ref_handle) + woal_reload_fw(ref_handle); + } +#endif + else + ret = -EFAULT; + if (ret) { + PRINTM(MERROR, "FW reload fail\n"); + goto done; + } + woal_post_reset(handle); + if (ref_handle) + woal_post_reset(ref_handle); +done: + LEAVE(); + return; +} + +/** Register to bus driver function */ +static mlan_status woal_bus_register(void) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef SDIO +#ifdef SDIO_MMC + /* Register SDIO driver */ + ret = woal_sdiommc_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register sdio_mmc bus driver\n"); + goto out; + } +#else + ret = woal_sdio_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register sdio_mmc bus driver\n"); + goto out; + } +#endif +#endif +#ifdef USB + /* Register USB driver */ + ret = woal_usb_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register usb bus driver\n"); + goto out; + } +#endif +#ifdef PCIE + /* Register PCIE driver */ + ret = woal_pcie_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register pcie bus driver\n"); + goto out; + } +#endif +out: + return ret; +} + +/** Unregister from bus driver function */ +static void woal_bus_unregister(void) +{ +#ifdef SDIO +#ifdef SDIO_MMC + /* Unregister SDIO driver */ + woal_sdiommc_bus_unregister(); +#else + woal_sdio_bus_unregister(); +#endif +#endif +#ifdef USB + /* Unregister USB driver */ + woal_usb_bus_unregister(); +#endif +#ifdef PCIE + /* Unregister PCIE driver */ + woal_pcie_bus_unregister(); +#endif +} + +/** + * @brief This function initializes module. + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int woal_init_module(void) +{ + int ret = (int)MLAN_STATUS_SUCCESS; + int index = 0; + + ENTER(); + + PRINTM(MMSG, "wlan: Loading MWLAN driver\n"); + /* Init the wlan_private pointer array first */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) + m_handle[index] = NULL; + /* Init mutex */ + MOAL_INIT_SEMAPHORE(&AddRemoveCardSem); + + if (woal_root_proc_init() != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "woal_init_module: Unable to create /proc/mwlan/ directory\n"); + LEAVE(); + return -EFAULT; + } + +#ifdef CONFIG_OF + woal_init_from_dev_tree(); +#endif + + /* Create workqueue for hang process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be greater than 10 + characters */ + hang_workqueue = create_workqueue("MOAL_HANG_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + hang_workqueue = + alloc_workqueue("MOAL_HANG_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + hang_workqueue = create_workqueue("MOAL_HANG_WORK_QUEUE"); +#endif +#endif + MLAN_INIT_WORK(&hang_work, woal_hang_work_queue); + + /* Register with bus */ + ret = woal_bus_register(); + if (ret == MLAN_STATUS_SUCCESS) + PRINTM(MMSG, "wlan: Driver loaded successfully\n"); + else + PRINTM(MMSG, "wlan: Driver loading failed\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void woal_cleanup_module(void) +{ + moal_handle *handle = NULL; + int index = 0; + int i; +#if defined(STA_SUPPORT) && defined(STA_CFG80211) + unsigned long flags; +#endif + + ENTER(); + + PRINTM(MMSG, "wlan: Unloading MWLAN driver\n"); + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + handle = m_handle[index]; + if (!handle) + continue; + if (!handle->priv_num) + goto exit; + if (MTRUE == woal_check_driver_status(handle)) + goto exit; + +#ifdef USB +#ifdef CONFIG_USB_SUSPEND + if (IS_USB(handle->card_type) && + handle->is_suspended == MTRUE) { + woal_exit_usb_suspend(handle); + } +#endif /* CONFIG_USB_SUSPEND */ +#endif + +#ifdef SDIO +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + if (handle->is_suspended == MTRUE) { + woal_sdio_resume( + &(((struct sdio_mmc_card *)handle->card)->func) + ->dev); + } +#endif /* MMC_PM_KEEP_POWER */ +#endif /* SDIO_SUSPEND_RESUME */ +#endif + + if (handle->rf_test_mode) + woal_process_rf_test_mode(handle, + MFG_CMD_UNSET_TEST_MODE); + + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == + MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) { + woal_disconnect(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT, + NULL, + DEF_DEAUTH_REASON_CODE); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211( + handle->params.cfg80211_wext) && + (handle->priv[i]->bss_type == + MLAN_BSS_TYPE_STA)) + woal_clear_conn_params(handle->priv[i]); + spin_lock_irqsave(&handle->scan_req_lock, + flags); + if (IS_STA_CFG80211( + handle->params.cfg80211_wext) && + handle->scan_request) { + woal_cfg80211_scan_done( + handle->scan_request, MTRUE); + handle->scan_request = NULL; + handle->scan_priv = NULL; + } + spin_unlock_irqrestore(&handle->scan_req_lock, + flags); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211( + handle->params.cfg80211_wext) && + handle->priv[i]->sched_scanning) { + woal_stop_bg_scan( + handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + handle->priv[i]->bg_scan_start = MFALSE; + handle->priv[i]->bg_scan_reported = + MFALSE; + cfg80211_sched_scan_stopped( + handle->priv[i]->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + handle->priv[i]->bg_scan_reqid +#endif + ); + handle->priv[i]->sched_scanning = + MFALSE; + } +#endif +#endif + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == + MLAN_BSS_ROLE_UAP) { +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != + MLAN_INIT_PARA_ENABLED) +#endif + { + woal_disconnect(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT, + NULL, + DEF_DEAUTH_REASON_CODE); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + woal_clear_all_mgmt_ies(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + woal_flush_tx_stat_queue(handle->priv[i]); +#endif + } + +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + { + woal_set_deep_sleep(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT_TIMEOUT, MFALSE, 0); + } + +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + { + woal_shutdown_fw(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } + } + +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + /* Unregister from bus */ + woal_bus_unregister(); + PRINTM(MMSG, "wlan: Driver unloaded\n"); + if (hang_workqueue) { + flush_workqueue(hang_workqueue); + destroy_workqueue(hang_workqueue); + hang_workqueue = NULL; + } + woal_root_proc_remove(); + + LEAVE(); +} + +#ifndef MODULE +#ifdef MFG_CMD_SUPPORT +/** + * @brief This function handle the mfg_mode from kernel boot command + * + * @param str buffer for mfg_mode + * @return N/A + */ +static int __init mfg_mode_setup(char *str) +{ + int val = -1; + get_option(&str, &val); + if (val > 0) + mfg_mode = 1; + PRINTM(MMSG, "mfg_mode=%d\n", mfg_mode); + return 1; +} +__setup("mfg_mode=", mfg_mode_setup); +#endif +#endif + +module_init(woal_init_module); +module_exit(woal_cleanup_module); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("NXP"); +MODULE_VERSION(MLAN_RELEASE_VERSION); +MODULE_LICENSE("GPL"); |