/** @file moal_main.c * * @brief This file contains the major functions in WLAN * driver. * * * Copyright 2008-2022 NXP * * NXP CONFIDENTIAL * The source code contained or described herein and all documents related to * the source code (Materials) are owned by NXP, its * suppliers and/or its licensors. Title to the Materials remains with NXP, * its suppliers and/or its licensors. The Materials contain * trade secrets and proprietary and confidential information of NXP, its * suppliers and/or its licensors. The Materials are protected by worldwide * copyright and trade secret laws and treaty provisions. No part of the * Materials may be used, copied, reproduced, modified, published, uploaded, * posted, transmitted, distributed, or disclosed in any way without NXP's prior * express written permission. * * No license under any patent, copyright, trade secret or other intellectual * property right is granted to or conferred upon you by disclosure or delivery * of the Materials, either expressly, by implication, inducement, estoppel or * otherwise. Any license under such intellectual property rights must be * express and approved by NXP in writing. * * Alternatively, this software may be distributed under the terms of GPL v2. * SPDX-License-Identifier: GPL-2.0 * */ /******************************************************** 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 #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) #include #endif #include #ifdef CONFIG_OF #include #endif #if IS_ENABLED(CONFIG_IPV6) #include #endif /******************************************************** Global Variables ********************************************************/ /** the pointer of new fwdump fname for each dump**/ static 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]; static int reg_work; /******************************************************** Local Variables ********************************************************/ #ifdef SD8801 static struct _card_info card_info_SD8801 = { .embedded_supp = 0, .drcs = 0, .go_noa = 0, .v14_fw_api = 1, .v16_fw_api = 0, .v17_fw_api = 0, .pmic = 0, .cal_data_cfg = 0, .low_power_enable = 1, .rx_rate_max = 76, .histogram_table_num = 1, .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), .fw_name = SD8801_DEFAULT_WLAN_FW_NAME, .fw_name_wlan = SD8801_DEFAULT_WLAN_FW_NAME, #ifdef SDIO .dump_fw_info = 0, .dump_fw_ctrl_reg = 0x63, .dump_fw_start_reg = 0x64, .dump_fw_end_reg = 0x6A, .dump_fw_host_ready = 0xee, .dump_reg.reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, .dump_reg.reg_table_size = 5, .scratch_reg = 0x60, .func1_reg_start = 0x10, .func1_reg_end = 0x17, .fw_reset_reg = 0x64, .fw_reset_val = 0, .slew_rate_reg = 0x8000231C, .slew_rate_bit_offset = 14, #endif .sniffer_support = 1, .per_pkt_cfg_support = 0, }; #endif #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 .sniffer_support = 0, .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 .sniffer_support = 0, .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, .sniffer_support = 0, .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, .sniffer_support = 0, .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 .sniffer_support = 1, .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 .sniffer_support = 1, .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 .sniffer_support = 1, .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 .sniffer_support = 1, .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 .sniffer_support = 1, .per_pkt_cfg_support = 1, }; #endif #ifdef SDNW62X static struct _card_info card_info_SDNW62X = { .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 = SDNW62X_DEFAULT_COMBO_FW_NAME, .fw_name_wlan = SDNW62X_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 .sniffer_support = 1, .per_pkt_cfg_support = 1, }; #endif #ifdef SD9177 static struct _card_info card_info_SD9177 = { .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 = SD9177_DEFAULT_COMBO_FW_NAME, .fw_name_wlan = SD9177_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 .sniffer_support = 1, .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, .sniffer_support = 1, .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, .sniffer_support = 1, .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, .sniffer_support = 1, .per_pkt_cfg_support = 1, }; #endif #ifdef PCIENW62X static struct _card_info card_info_PCIENW62X = { .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 = PCIENW62X_DEFAULT_COMBO_FW_NAME, .fw_name_wlan = PCIENW62X_DEFAULT_WLAN_FW_NAME, .sniffer_support = 1, .per_pkt_cfg_support = 1, }; #endif #ifdef USB8801 static struct _card_info card_info_USB8801 = { .embedded_supp = 0, .drcs = 0, .go_noa = 0, .v14_fw_api = 1, .v16_fw_api = 0, .v17_fw_api = 0, .pmic = 0, .cal_data_cfg = 0, .low_power_enable = 1, .rx_rate_max = 76, .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), .histogram_table_num = 1, .fw_name = USB8801_DEFAULT_WLAN_FW_NAME, .fw_name_wlan = USB8801_DEFAULT_WLAN_FW_NAME, .sniffer_support = 1, .per_pkt_cfg_support = 0, }; #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, .sniffer_support = 1, .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, .sniffer_support = 1, .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, .sniffer_support = 1, .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, .sniffer_support = 1, .per_pkt_cfg_support = 1, }; #endif #ifdef USBNW62X static struct _card_info card_info_USBNW62X = { .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 = USBNW62X_DEFAULT_COMBO_FW_NAME, .fw_name_wlan = USBNW62X_DEFAULT_WLAN_FW_NAME, .sniffer_support = 1, .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 .sniffer_support = 1, .per_pkt_cfg_support = 1, }; #endif /** Driver version */ char driver_version[] = INTF_CARDTYPE KERN_VERSION "--" MLAN_RELEASE_VERSION "-(" "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_amsdu_packet = moal_recv_amsdu_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, .moal_updata_peer_signal = moal_updata_peer_signal, .moal_do_div = moal_do_div, #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, .moal_amsdu_tp_accounting = moal_amsdu_tp_accounting, }; 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); int woal_change_mtu(struct net_device *dev, int new_mtu); 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; /** register workqueue */ static struct workqueue_struct *register_workqueue; /** register work */ static struct work_struct register_work; /** * @brief This function send fw dump event to kernel * * @param priv Pointer to structure moal_private * * @return N/A */ void woal_send_fw_dump_complete_event(moal_private *priv) { int cfg80211_wext = priv->phandle->params.cfg80211_wext; #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_FW_DUMP_DONE); #endif #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) woal_cfg80211_vendor_event_fw_dump(priv); #endif woal_broadcast_event(priv, CUS_EVT_FW_DUMP_DONE, strlen(CUS_EVT_FW_DUMP_DONE)); return; } /** * @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; moal_private *priv; int cfg80211_wext = 0; ENTER(); if (!reset_handle) { LEAVE(); return; } cfg80211_wext = reset_handle->params.cfg80211_wext; // stop pending scan #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext) && reset_handle->scan_request) { moal_private *scan_priv = reset_handle->scan_priv; /** some supplicant can not handle SCAN abort event */ if (scan_priv->bss_type == MLAN_BSS_TYPE_STA) woal_cfg80211_scan_done(reset_handle->scan_request, MTRUE); else woal_cfg80211_scan_done(reset_handle->scan_request, MFALSE); reset_handle->scan_request = NULL; reset_handle->scan_priv = NULL; reset_handle->scan_pending_on_block = MFALSE; MOAL_REL_SEMAPHORE(&reset_handle->async_sem); } #endif for (i = 0; i < reset_handle->priv_num; i++) { if (reset_handle->priv[i]) { priv = reset_handle->priv[i]; woal_stop_queue(priv->netdev); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); priv->media_connected = MFALSE; // disconnect #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev->current_bss) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) if (priv->host_mlme) woal_host_mlme_disconnect( priv, MLAN_REASON_DEAUTH_LEAVING, NULL); else #endif cfg80211_disconnected(priv->netdev, 0, NULL, 0, #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) true, #endif GFP_KERNEL); } #endif #endif // stop bgscan #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) if (IS_STA_CFG80211(cfg80211_wext) && priv->sched_scanning) { 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 } } mlan_ioctl(reset_handle->pmlan_adapter, NULL); woal_flush_workqueue(reset_handle); priv = woal_get_priv(reset_handle, MLAN_BSS_ROLE_ANY); if (priv) { woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, strlen(CUS_EVT_DRIVER_HANG)); #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) woal_cfg80211_vendor_event(priv, event_hang, CUS_EVT_DRIVER_HANG, strlen(CUS_EVT_DRIVER_HANG)); #endif #endif } 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; 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++) { if (!handle->priv[i]) continue; #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; } #ifdef STA_CFG80211 /** @brief This function set/clear pmk to FW * * @param priv A Pointer to the moal_private structure * @param action set/clear action * * @return 0: success fail otherwise */ int woal_set_clear_pmk(moal_private *priv, t_u8 action) { mlan_ioctl_req *req; mlan_ds_sec_cfg *sec; mlan_status status; int ret = 0; t_u8 zero[MLAN_MAX_KEY_LENGTH] = {0}; ENTER(); req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); if (req == NULL) { ret = -ENOMEM; } else { sec = (mlan_ds_sec_cfg *)req->pbuf; sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; req->req_id = MLAN_IOCTL_SEC_CFG; req->action = action; if (action == MLAN_ACT_SET) { sec->param.passphrase.psk_type = MLAN_PSK_PMK; if (memcmp(priv->pmk.pmk, zero, MLAN_MAX_KEY_LENGTH)) moal_memcpy_ext( priv->phandle, &sec->param.passphrase.psk.pmk.pmk, priv->pmk.pmk, MLAN_MAX_KEY_LENGTH, sizeof(sec->param.passphrase.psk.pmk .pmk)); if (memcmp(priv->pmk.pmk_r0, zero, MLAN_MAX_KEY_LENGTH) && memcmp(priv->pmk.pmk_r0_name, zero, MLAN_MAX_PMKR0_NAME_LENGTH)) { moal_memcpy_ext( priv->phandle, &sec->param.passphrase.psk.pmk.pmk_r0, priv->pmk.pmk_r0, MLAN_MAX_KEY_LENGTH, sizeof(sec->param.passphrase.psk.pmk .pmk_r0)); moal_memcpy_ext( priv->phandle, &sec->param.passphrase.psk.pmk .pmk_r0_name, priv->pmk.pmk_r0_name, MLAN_MAX_PMKR0_NAME_LENGTH, sizeof(sec->param.passphrase.psk.pmk .pmk_r0_name)); } } status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); if (MLAN_STATUS_SUCCESS != status) ret = -EFAULT; if (status != MLAN_STATUS_PENDING) kfree(req); } LEAVE(); return ret; } #endif /** * @brief This function handle the net interface ipaddr change event * * @param nb pointer to the notifier_block * @param event event type * @param ptr pointer to event struct * * @return NOTIFY_DONE or NOTIFY_OK */ static int woal_netdevice_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; struct net_device *ndev; moal_private *priv; int ret = NOTIFY_OK; #ifdef STA_CFG80211 char rssi_low[11]; #endif ENTER(); ndev = ifa->ifa_dev->dev; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) if (!ndev || ndev->netdev_ops->ndo_open != woal_open) #else if (!ndev || ndev->open != woal_open) #endif { PRINTM(MIOCTL, "IP changes not for us, ignore. ndev[%p]\n", ndev); if (ndev) PRINTM(MIOCTL, "changes on %s\n", ndev->name); ret = NOTIFY_DONE; goto done; } priv = (moal_private *)netdev_priv(ndev); if (priv->bss_type != MLAN_BSS_TYPE_STA #ifdef WIFI_DIRECT_SUPPORT && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT #endif ) { PRINTM(MIOCTL, "Bss type [%d] is not STA/P2P, ignore\n", (int)priv->bss_type); ret = NOTIFY_DONE; goto done; } switch (event) { case NETDEV_UP: PRINTM(MIOCTL, "[%s]: New ip addr: 0x%08x\n", ndev->name, ifa->ifa_address); /* Save the IP addr now */ moal_memcpy_ext(priv->phandle, priv->ip_addr, &ifa->ifa_address, sizeof(ifa->ifa_address), sizeof(priv->ip_addr)); priv->ip_addr_type = IPADDR_TYPE_IPV4; #ifdef STA_CFG80211 if (!moal_extflg_isset(priv->phandle, EXT_HW_TEST) && priv->roaming_enabled) { snprintf(rssi_low, sizeof(rssi_low), "%d", priv->rssi_low); woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT); } #endif #ifdef STA_CFG80211 if (priv->phandle->fw_roam_enable && (priv->phandle->fw_roam_enable != AUTO_RECONNECT) && !moal_extflg_isset(priv->phandle, EXT_ROAMOFFLOAD_IN_HS)) { snprintf(rssi_low, sizeof(rssi_low), "%d", priv->rssi_low); woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT); if (priv->pmk_saved) { woal_set_clear_pmk(priv, MLAN_ACT_SET); priv->pmk_saved = false; } } #endif break; case NETDEV_DOWN: PRINTM(MIOCTL, "[%s]: Ip addr removed.\n", ndev->name); priv->ip_addr_type = IPADDR_TYPE_NONE; memset(priv->ip_addr, 0, sizeof(priv->ip_addr)); break; default: PRINTM(MIOCTL, "[%s]: Ignore event: %u\n", ndev->name, (unsigned int)event); ret = NOTIFY_DONE; goto done; } done: LEAVE(); return ret; } #if IS_ENABLED(CONFIG_IPV6) /** * @brief This function handle the net interface ipv6 address change event * * @param nb pointer to the notifier_block * @param event event type * @param ptr pointer to event struct * * @return NOTIFY_DONE or NOTIFY_OK */ static int woal_inet6_netdeive_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; struct net_device *ndev = ifa->idev->dev; moal_private *priv; int ret = NOTIFY_OK; ENTER(); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) if (!ndev || ndev->netdev_ops->ndo_open != woal_open) #else if (!ndev || ndev->open != woal_open) #endif { PRINTM(MIOCTL, "IPV6 changes not for us, ignore. ndev[%p]\n", ndev); if (ndev) PRINTM(MIOCTL, "changes on %s\n", ndev->name); ret = NOTIFY_DONE; goto done; } priv = (moal_private *)netdev_priv(ndev); if (!priv) { PRINTM(MERROR, "Invalid private structure\n"); goto done; } if (priv->bss_type != MLAN_BSS_TYPE_STA #ifdef WIFI_DIRECT_SUPPORT && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT #endif ) { PRINTM(MIOCTL, "Bss type [%d] is not STA/P2P, ignore\n", (int)priv->bss_type); ret = NOTIFY_DONE; goto done; } switch (event) { case NETDEV_UP: PRINTM(MIOCTL, "[%s]: New ipv6 addr\n", ndev->name); moal_memcpy_ext(priv->phandle, priv->ipv6_addr, (t_u8 *)&ifa->addr, sizeof(priv->ipv6_addr), sizeof(priv->ipv6_addr)); priv->ipv6_addr_configured = MTRUE; break; case NETDEV_DOWN: PRINTM(MIOCTL, "[%s]: Ipv6 addr removed.\n", ndev->name); memset(priv->ipv6_addr, 0, sizeof(priv->ipv6_addr)); priv->ipv6_addr_configured = MFALSE; break; default: PRINTM(MIOCTL, "[%s]: Ignore event: %u\n", ndev->name, (unsigned int)event); ret = NOTIFY_DONE; goto done; } done: LEAVE(); return ret; } #endif /** * @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) { 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) { status = MLAN_STATUS_FAILURE; 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 int max_dfs_bss = MAX_DFS_BSS; 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 */ if (drv_mode_local & DRV_MODE_DFS) intf_num += max_dfs_bss; /* 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 >= (int)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 >= (int)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 >= (int)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 */ if (drv_mode_local & DRV_MODE_DFS) { for (j = 0; j < max_dfs_bss; j++) { if (i >= (int)intf_num) break; bss_tbl[i].bss_type = MLAN_BSS_TYPE_DFS; 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++; } } #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 >= (int)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 #if defined(STA_CFG80211) && defined(UAP_CFG80211) handle->mon_if = NULL; #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 */ /* Initialize the timer for the FW dump*/ woal_initialize_timer(&handle->fw_dump_timer, woal_fw_dump_timer_func, handle); handle->is_fw_dump_timer_set = MFALSE; #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.ext_scan = (t_u8)handle->params.ext_scan; 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 PCIE if (IS_PCIE(handle->card_type)) device.ring_size = handle->params.ring_size; #endif #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 (!handle->params.amsdu_deaggr) device.callbacks.moal_recv_amsdu_packet = NULL; 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); woal_cfg80211_free_bands(handle->wiphy); 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 >= (t_s32)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 < (t_s32)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; t_u32 reg_type = MLAN_REG_MAC; 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; #if defined(SD9098) || defined(SD9097) || defined(SDNW62X) || defined(SD9177) if (IS_SD9098(handle->card_type) || IS_SD9097(handle->card_type) || IS_SDNW62X(handle->card_type) || IS_SD9177(handle->card_type)) reg_type = MLAN_REG_CIU; #endif ret = woal_getset_regrdwr(priv, MLAN_ACT_GET, reg_type, 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, reg_type, 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 ((int)(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; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) eth_hw_addr_set( handle->priv[i]->netdev, handle->priv[i] ->current_addr); #else moal_memcpy_ext( handle, handle->priv[i] ->netdev ->dev_addr, handle->priv[i] ->current_addr, ETH_ALEN, ETH_ALEN); #endif 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 */ memcpy(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 */ static 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(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, vdll_fw, handle->hotplug_device, GFP_KERNEL, handle, woal_request_vdll_fw_callback)) < 0) { #else #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 #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) { PRINTM(MMSG, "Request firmware: %s\n", init_cfg); if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, init_cfg, handle->hotplug_device, GFP_KERNEL, handle, woal_request_init_cfg_data_callback)) < 0) { #else #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 #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; } PRINTM(MMSG, "Request firmware: %s\n", country_txpwrlimit); /* 'country_txpwrlimit' holds the value of Configured Tx Power * Limit */ if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, country_txpwrlimit, handle->hotplug_device, GFP_KERNEL, handle, woal_request_init_user_conf_callback)) < 0) { #else #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 #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 { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) int status = request_firmware_direct(&handle->user_data, country_txpwrlimit, handle->hotplug_device); #else int status = request_firmware(&handle->user_data, country_txpwrlimit, handle->hotplug_device); #endif /* 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) { PRINTM(MMSG, "Request firmware: %s\n", init_hostcmd_cfg); if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, 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, 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 #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) { PRINTM(MMSG, "Request firmware: %s\n", band_steer_cfg); if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, 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, 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 #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; } static int woal_netdevice_event(struct notifier_block *nb, unsigned long event, void *ptr); #ifdef UAP_SUPPORT /** * @brief Configure WACP 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_wacp_mode(moal_private *priv, t_u8 wait_option) { // moal_private *priv = NULL; mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *pcfg_misc = NULL; mlan_status status; ENTER(); /* 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_WACP_MODE; req->req_id = MLAN_IOCTL_MISC_CFG; req->action = MLAN_ACT_SET; pcfg_misc->param.wacp_mode = priv->phandle->params.wacp_mode; /* Send IOCTL request to MLAN */ status = woal_request_ioctl(priv, req, wait_option); done: if (status != MLAN_STATUS_PENDING) kfree(req); LEAVE(); return status; } #endif /** * @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 */ static 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); handle->woal_notifier.notifier_call = woal_netdevice_event; if (register_inetaddr_notifier(&handle->woal_notifier)) { PRINTM(MFATAL, "Error registering register_inetaddr_notifier\n"); goto err; } #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"); } /* Add channel tracking check */ woal_set_chan_track_mode(handle, MOAL_IOCTL_WAIT); #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 #if IS_ENABLED(CONFIG_IPV6) handle->woal_inet6_notifier.notifier_call = woal_inet6_netdeive_event; if (register_inet6addr_notifier(&handle->woal_inet6_notifier)) { PRINTM(MFATAL, "Error registering register_inet6addr_notifier\n"); goto err; } #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"); unregister_inetaddr_notifier(&handle->woal_notifier); #if IS_ENABLED(CONFIG_IPV6) unregister_inet6addr_notifier(&handle->woal_inet6_notifier); #endif 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"))) { PRINTM(MMSG, "Request firmware: %s\n", dpd_data_cfg); if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, 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, 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 #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"))) { PRINTM(MMSG, "Download txpwrlimit_cfg=%s\n", handle->params.txpwrlimit_cfg); if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, txpwrlimit_cfg, handle->hotplug_device, GFP_KERNEL, handle, woal_request_init_txpwr_conf_callback)) < 0) { #else #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 #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"))) { PRINTM(MMSG, "Request firmware: %s\n", cal_data_cfg); if (req_fw_nowait) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) if ((request_firmware_nowait( THIS_MODULE, FW_ACTION_UEVENT, 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, 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 #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_USBNW62X(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"); if (!handle->fw_reload) 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 */ ret = mlan_set_init_param(handle->pmlan_adapter, ¶m); if (handle->fw_reload) { LEAVE(); return ret; } handle->init_wait_q_woken = MFALSE; 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, 10 * 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(5, 14, 0) err = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, handle->drv_mode.fw_name, handle->hotplug_device, GFP_KERNEL, handle, woal_request_fw_callback); #else #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 #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; dot11_txcontrol *txcontrol; t_u8 tx_ctrl_flag = MFALSE; int i = 0; ENTER(); eth = (struct ethhdr *)skb->data; switch (eth->h_proto) { case __constant_htons(ETH_P_IP): PRINTM(MINFO, "packet type ETH_P_IP: %04x, prio=%#x\n", eth->h_proto, skb->priority); break; case __constant_htons(ETH_P_IPV6): PRINTM(MINFO, "packet type ETH_P_IPV6: %04x, prio=%#x\n", eth->h_proto, skb->priority); break; case __constant_htons(ETH_P_ARP): skb->priority = 0; PRINTM(MINFO, "ARP packet %04x prio=%#x\n", eth->h_proto, skb->priority); break; default: skb->priority = 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; skb->priority = 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; } PRINTM(MDAT_D, "packet %04x prio=%#x\n", eth->h_proto, skb->priority); if (priv->enable_mc_aggr && priv->num_mcast_addr) { if (woal_find_mcast_node_tx(priv, skb)) { mc_txcontrol *tx_ctrl = (mc_txcontrol *)(skb->data + skb->len - sizeof(mc_txcontrol)); moal_memcpy_ext(priv->phandle, (t_u8 *)&pmbuf->u.mc_tx_info, (t_u8 *)tx_ctrl, sizeof(mc_txcontrol), sizeof(mc_txcontrol)); pmbuf->flags |= MLAN_BUF_FLAG_MC_AGGR_PKT; // use AC_VO skb->priority = 6; } } /* 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); skb->tstamp = ktime_get_real(); 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 defined(STA_CFG80211) && defined(UAP_CFG80211) /** * @brief This function opens the network device for monitor interface * * @param dev A pointer to net_device structure * * @return 0 -- success, otherwise fail */ static int woal_mon_open(struct net_device *ndev) { ENTER(); LEAVE(); return 0; } /** * @brief This function closes the network device for monitor interface * * @param dev A pointer to net_device structure * * @return 0 -- success, otherwise fail */ static int woal_mon_close(struct net_device *ndev) { ENTER(); LEAVE(); return 0; } /** * @brief This function sets the MAC address to firmware for monitor interface * * @param dev A pointer to net_device structure * @param addr MAC address to set * * @return 0 -- success, otherwise fail */ static int woal_mon_set_mac_address(struct net_device *ndev, void *addr) { ENTER(); LEAVE(); return 0; } /** * @brief This function sets multicast address to firmware for monitor interface * * @param dev A pointer to net_device structure * * @return 0 -- success, otherwise fail */ static void woal_mon_set_multicast_list(struct net_device *ndev) { ENTER(); LEAVE(); } /** * @brief This function handles packet transmission for monitor interface * * @param skb A pointer to sk_buff structure * @param dev A pointer to net_device structure * * @return 0 -- success, otherwise fail */ static netdev_tx_t woal_mon_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) { int len_rthdr; int qos_len = 0; int dot11_hdr_len = 24; int snap_len = 6; unsigned char *pdata; unsigned short fc; unsigned char src_mac_addr[6]; unsigned char dst_mac_addr[6]; struct ieee80211_hdr *dot11_hdr; struct ieee80211_radiotap_header *prthdr = (struct ieee80211_radiotap_header *)skb->data; monitor_iface *mon_if = netdev_priv(ndev); ENTER(); if (mon_if == NULL || mon_if->base_ndev == NULL) { goto fail; } /* check for not even having the fixed radiotap header part */ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) { PRINTM(MERROR, "Invalid radiotap hdr length," "skb->len: %d\n", skb->len); goto fail; /* too short to be possibly valid */ } /* is it a header version we can trust to find length from? */ if (unlikely(prthdr->it_version)) goto fail; /* only version 0 is supported */ /* then there must be a radiotap header with a length we can use */ len_rthdr = ieee80211_get_radiotap_len(skb->data); /* does the skb contain enough to deliver on the alleged length? */ if (unlikely((int)skb->len < len_rthdr)) { PRINTM(MERROR, "Invalid data length," "skb->len: %d\n", skb->len); goto fail; /* skb too short for claimed rt header extent */ } /* Skip the ratiotap header */ skb_pull(skb, len_rthdr); dot11_hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(dot11_hdr->frame_control); if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { /* Check if this ia a Wireless Distribution System (WDS) frame * which has 4 MAC addresses */ if (dot11_hdr->frame_control & (__force __le16)0x0080) qos_len = 2; if ((dot11_hdr->frame_control & (__force __le16)0x0300) == (__force __le16)0x0300) dot11_hdr_len += 6; moal_memcpy_ext(NULL, dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr), sizeof(dst_mac_addr)); moal_memcpy_ext(NULL, src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr), sizeof(src_mac_addr)); /* Skip the 802.11 header, QoS (if any) and SNAP, but leave * spaces for for two MAC addresses */ skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); pdata = (unsigned char *)skb->data; moal_memcpy_ext(NULL, pdata, dst_mac_addr, sizeof(dst_mac_addr), (t_u32)skb->len); moal_memcpy_ext(NULL, pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr), (t_u32)skb->len - sizeof(dst_mac_addr)); LEAVE(); return woal_hard_start_xmit(skb, mon_if->base_ndev); } fail: dev_kfree_skb(skb); LEAVE(); return NETDEV_TX_OK; } /** * @brief This function returns the network statistics * * @param dev A pointer to net_device structure * * @return A pointer to net_device_stats structure */ static struct net_device_stats *woal_mon_get_stats(struct net_device *dev) { monitor_iface *mon_if = (monitor_iface *)netdev_priv(dev); return &mon_if->stats; } static const struct net_device_ops woal_cfg80211_mon_if_ops = { .ndo_open = woal_mon_open, .ndo_start_xmit = woal_mon_hard_start_xmit, .ndo_stop = woal_mon_close, .ndo_get_stats = woal_mon_get_stats, .ndo_set_mac_address = woal_mon_set_mac_address, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) .ndo_set_rx_mode = woal_mon_set_multicast_list, #else .ndo_set_multicast_list = woal_mon_set_multicast_list, #endif }; /** * @brief This function setup monitor interface * * @param dev A pointer to net_device structure * * @return 0 -- success, otherwise fail */ static void woal_mon_if_setup(struct net_device *dev) { ENTER(); ether_setup(dev); dev->netdev_ops = &woal_cfg80211_mon_if_ops; #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 9) dev->needs_free_netdev = true; #else dev->destructor = free_netdev; #endif LEAVE(); } /** * @brief Request the driver to add a monitor interface * * @param priv A pointer to moal_private * @param name Virtual interface name * @param name_assign_type Interface name assignment type * @param sniffer_mode Sniffer mode * * @return A pointer to monitor_iface */ monitor_iface *woal_prepare_mon_if(moal_private *priv, const char *name, unsigned char name_assign_type, int sniffer_mode) { int ret = 0; moal_handle *handle = priv->phandle; struct net_device *ndev = NULL; monitor_iface *mon_if = NULL; ENTER(); if (sniffer_mode != CHANNEL_SPEC_SNIFFER_MODE) { PRINTM(MERROR, "Sniffer mode is not valid\n"); ret = -EFAULT; goto fail; } if ((sniffer_mode == CHANNEL_SPEC_SNIFFER_MODE) && woal_is_any_interface_active(handle)) { PRINTM(MERROR, "Cannot start channel specified net monitor when Interface Active\n"); ret = -EFAULT; goto fail; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) ndev = alloc_netdev_mq(sizeof(*mon_if), name, name_assign_type, woal_mon_if_setup, 1); #else ndev = alloc_netdev_mq(sizeof(*mon_if), name, NET_NAME_UNKNOWN, woal_mon_if_setup, 1); #endif #else ndev = alloc_netdev_mq(sizeof(*mon_if), name, woal_mon_if_setup, 1); #endif #else ndev = alloc_netdev_mq(sizeof(*mon_if), name, woal_mon_if_setup); #endif if (!ndev) { PRINTM(MFATAL, "Init virtual ethernet device failed\n"); ret = -EFAULT; goto fail; } ret = dev_alloc_name(ndev, ndev->name); if (ret < 0) { PRINTM(MFATAL, "Net device alloc name fail.\n"); ret = -EFAULT; goto fail; } //?memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); mon_if = netdev_priv(ndev); moal_memcpy_ext(handle, mon_if->ifname, ndev->name, IFNAMSIZ, IFNAMSIZ); ndev->type = ARPHRD_IEEE80211_RADIOTAP; ndev->netdev_ops = &woal_cfg80211_mon_if_ops; mon_if->priv = priv; mon_if->mon_ndev = ndev; mon_if->base_ndev = priv->netdev; mon_if->sniffer_mode = sniffer_mode; mon_if->radiotap_enabled = 1; mon_if->flag = 1; fail: if (ret) { if (ndev) free_netdev(ndev); LEAVE(); return NULL; } LEAVE(); return mon_if; } #endif #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, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) .ndo_siocdevprivate = woal_do_ioctl, #else .ndo_do_ioctl = woal_do_ioctl, #endif .ndo_set_mac_address = woal_set_mac_address, .ndo_change_mtu = woal_change_mtu, .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 #define MAX_MTU_SIZE 2000 /** * @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) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) mlan_fw_info fw_info; #endif ENTER(); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) memset(&fw_info, 0, sizeof(mlan_fw_info)); woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); if (fw_info.tx_buf_size > (MAX_MTU_SIZE + MLAN_MIN_DATA_HEADER_LEN + priv->extra_tx_head_len)) { dev->max_mtu = MAX_MTU_SIZE; PRINTM(MMSG, "wlan: %s set max_mtu %d\n", dev->name, dev->max_mtu); } #endif /* 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, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) .ndo_siocdevprivate = woal_uap_do_ioctl, #else .ndo_do_ioctl = woal_uap_do_ioctl, #endif .ndo_set_mac_address = woal_set_mac_address, .ndo_change_mtu = woal_change_mtu, .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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) mlan_fw_info fw_info; #endif ENTER(); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) memset(&fw_info, 0, sizeof(mlan_fw_info)); woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); if (fw_info.tx_buf_size > (MAX_MTU_SIZE + MLAN_MIN_DATA_HEADER_LEN + priv->extra_tx_head_len)) { dev->max_mtu = MAX_MTU_SIZE; PRINTM(MMSG, "wlan: %s set max_mtu %d\n", dev->name, dev->max_mtu); } #endif /* 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 /** don't need register to wext */ if (priv->bss_type == MLAN_BSS_TYPE_DFS) { LEAVE(); return status; } #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 STA_CFG80211 char sched_scan_str[256]; #endif #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 memset(name, 0, sizeof(name)); if (handle->second_mac) snprintf(name, sizeof(name), "m%s", default_dfs_name); else snprintf(name, sizeof(name), "%s", default_dfs_name); if ((bss_type == MLAN_BSS_TYPE_DFS) && (dev_alloc_name(dev, name) < 0)) { PRINTM(MERROR, "Could not allocate DFS device name\n"); goto error; } 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 else if (bss_type == MLAN_BSS_TYPE_DFS) priv->bss_role = MLAN_BSS_ROLE_UAP; INIT_LIST_HEAD(&priv->tcp_sess_queue); spin_lock_init(&priv->tcp_sess_lock); #ifdef STA_SUPPORT INIT_LIST_HEAD(&priv->tdls_list); spin_lock_init(&priv->tdls_lock); #endif INIT_LIST_HEAD(&priv->tx_stat_queue); spin_lock_init(&priv->tx_stat_lock); INIT_LIST_HEAD(&priv->mcast_list); spin_lock_init(&priv->mcast_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 || bss_type == MLAN_BSS_TYPE_DFS) { 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; } } strcpy(sched_scan_str, "SCHED_SCAN"); strcat(sched_scan_str, name); priv->sched_scan_workqueue = alloc_workqueue( sched_scan_str, WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); if (!priv->sched_scan_workqueue) { PRINTM(MERROR, "cannot alloc sched_scan workqueue \n"); goto error; } INIT_DELAYED_WORK(&priv->sched_scan_work, woal_sched_scan_work_queue); #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) && bss_type != MLAN_BSS_TYPE_DFS) { /* 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 */ /* Create workqueue for main process */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) priv->mclist_workqueue = alloc_workqueue("MCLIST_WORK_QUEUE", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); #else priv->mclist_workqueue = create_workqueue("MCLIST_WORK_QUEUE"); #endif if (!priv->mclist_workqueue) { PRINTM(MERROR, "cannot alloc mclist workqueue \n"); goto error; } MLAN_INIT_WORK(&priv->mclist_work, woal_mclist_work_queue); INIT_DELAYED_WORK(&priv->scan_deferred_work, woal_scan_deferred_work_queue); /* 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; /* Unregister wiphy device and free */ if (priv) { if (priv->mclist_workqueue) { flush_workqueue(priv->mclist_workqueue); destroy_workqueue(priv->mclist_workqueue); priv->mclist_workqueue = NULL; } #ifdef STA_CFG80211 if (priv->sched_scan_workqueue) { destroy_workqueue(priv->sched_scan_workqueue); priv->sched_scan_workqueue = NULL; } #endif #if defined(STA_CFG80211) || defined(UAP_CFG80211) 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; moal_connection_status_check_pmqos(handle); #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) woal_flush_tdls_list(priv); #endif woal_flush_mcast_list(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 (priv->mclist_workqueue) { flush_workqueue(priv->mclist_workqueue); destroy_workqueue(priv->mclist_workqueue); priv->mclist_workqueue = NULL; } #ifdef STA_CFG80211 if (priv->sched_scan_workqueue) { destroy_workqueue(priv->sched_scan_workqueue); priv->sched_scan_workqueue = NULL; } #endif #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 defined(STA_CFG80211) || defined(UAP_CFG80211) #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 #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; #if defined(STA_CFG80211) || defined(UAP_CFG80211) woal_cfg80211_notify_antcfg(priv, priv->phandle->wiphy, radio); #endif } 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 Configure MLAN for channel tracking 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_chan_track_mode(moal_handle *handle, t_u8 wait_option) { moal_private *priv = NULL; mlan_ioctl_req *req = NULL; mlan_ds_snmp_mib *mib = 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_snmp_mib)); if (req == NULL) { status = MLAN_STATUS_FAILURE; goto done; } /* Fill request buffer */ mib = (mlan_ds_snmp_mib *)req->pbuf; mib->sub_command = MLAN_OID_SNMP_MIB_CHAN_TRACK; mib->param.chan_track = moal_extflg_isset(handle, EXT_CHAN_TRACK); req->req_id = MLAN_IOCTL_SNMP_MIB; 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 */ static 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 flush all works in the queue * * @param handle A pointer to moal_handle * * @return N/A */ void woal_flush_workqueue(moal_handle *handle) { ENTER(); /* Terminate main workqueue */ if (handle->workqueue) flush_workqueue(handle->workqueue); if (handle->rx_workqueue) flush_workqueue(handle->rx_workqueue); if (handle->evt_workqueue) flush_workqueue(handle->evt_workqueue); if (handle->tx_workqueue) flush_workqueue(handle->tx_workqueue); LEAVE(); } /** * @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; #else // struct usb_device *udev = ((struct usb_card_rec // *)(priv->phandle->card))->udev; #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) /* Error enabling PM on interface */ if (usb_autopm_get_interface(intf)) { LEAVE(); return -EIO; } #else // atomic_set(&udev->dev.power.usage_count, 1); #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; #else // struct usb_device *udev = ((struct usb_card_rec // *)(priv->phandle->card))->udev; #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) usb_autopm_put_interface(intf); #else // atomic_set(&udev->dev.power.usage_count, 0); #endif /* < 2.6.34 */ } #endif /* USB_SUSPEND_RESUME */ LEAVE(); return 0; } #define DEF_MTU_SIZE 1500 /** * @brief This function disable the ampdu * * @param priv A pointer to mlan_private structure * @param new_mtu new mtu size * * @return 0 --success, otherwise fail */ void woal_disable_ampdu(moal_private *priv) { mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; int i; memset(&aggr_prio_tbl, 0, sizeof(aggr_prio_tbl)); if (MLAN_STATUS_SUCCESS != woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_GET, &aggr_prio_tbl)) { goto done; } for (i = 0; i < MAX_NUM_TID; i++) aggr_prio_tbl.ampdu[i] = 0xff; if (MLAN_STATUS_SUCCESS != woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, &aggr_prio_tbl)) { goto done; } done: return; } /** * @brief This function change the MTU size * * @param dev A pointer to mlan_private structure * @param new_mtu new mtu size * * @return 0 --success, otherwise fail */ int woal_change_mtu(struct net_device *dev, int new_mtu) { moal_private *priv = (moal_private *)netdev_priv(dev); dev->mtu = new_mtu; // disable AMPDU with mtu size > 1500 if (new_mtu > DEF_MTU_SIZE) woal_disable_ampdu(priv); PRINTM(MCMND, "wlan: change_mtu to %d\n", new_mtu); 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); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) eth_hw_addr_set(dev, priv->current_addr); #else moal_memcpy_ext(priv->phandle, dev->dev_addr, priv->current_addr, ETH_ALEN, ETH_ALEN); #endif 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_data_port_mask=0x%x\n", info->mp_data_port_mask); PRINTM(MERROR, "last_recv_rd_bitmap=0x%x mp_invalid_update=%d\n", info->last_recv_rd_bitmap, 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 & (info->txrx_bd_size - 1)); PRINTM(MERROR, "TxRx BD size:%d\n", info->txrx_bd_size); } #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, "bypass_pkt_count=%d\n", info->bypass_pkt_count); PRINTM(MERROR, "data_sent=%d cmd_sent=%d\n", info->data_sent, info->cmd_sent); PRINTM(MERROR, "data_sent_cnt=%u\n", info->data_sent_cnt); 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 < (int)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 */ static 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); t_u8 auto_fw_dump = MFALSE; 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) { if (drvdbg & MFW_D) auto_fw_dump = MTRUE; woal_mlan_debug_info(priv); woal_moal_debug_info(priv, NULL, MFALSE); priv->phandle->driver_status = MTRUE; if (!auto_fw_dump && !priv->phandle->fw_dump) 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; } /** * @brief This function determine the 802.1p/1d tag to use * * @param skb * * @return tid */ unsigned int woal_classify8021d(struct sk_buff *skb) { unsigned int dscp; unsigned char vlan_priority; unsigned int tid; /* 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 (skb->priority >= 256 && skb->priority <= 263) { tid = skb->priority - 256; goto out; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) if (skb_vlan_tag_present(skb)) { vlan_priority = (skb_vlan_tag_get(skb) & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; if (vlan_priority > 0) { tid = vlan_priority; goto out; } } #endif switch (skb->protocol) { case htons(ETH_P_IP): dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc; break; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) case htons(ETH_P_IPV6): dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc; break; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) case htons(ETH_P_MPLS_UC): case htons(ETH_P_MPLS_MC): { struct mpls_label mpls_tmp, *mpls; mpls = skb_header_pointer(skb, sizeof(struct ethhdr), sizeof(*mpls), &mpls_tmp); if (!mpls) return 0; tid = (ntohl(mpls->entry) & MPLS_LS_TC_MASK) >> MPLS_LS_TC_SHIFT; goto out; } case htons(ETH_P_80221): /* 802.21 is always network control traffic */ return 7; #endif default: return 0; } tid = dscp >> 5; out: return tid; } #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); t_u8 tid = 0; t_u8 index = 0; ENTER(); if (priv->phandle->surprise_removed == MTRUE) { LEAVE(); return index; } #if defined(STA_CFG80211) || defined(UAP_CFG80211) tid = skb->priority = cfg80211_classify8021d(skb, NULL); #else tid = skb->priority = woal_classify8021d(skb); #endif 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 mcast list * * @param priv A pointer to moal_private structure * * @return N/A */ void woal_flush_mcast_list(moal_private *priv) { struct mcast_node *node = NULL, *tmp_node; unsigned long flags; spin_lock_irqsave(&priv->mcast_lock, flags); list_for_each_entry_safe (node, tmp_node, &priv->mcast_list, link) { list_del(&node->link); kfree(node); } INIT_LIST_HEAD(&priv->mcast_list); priv->num_mcast_addr = 0; spin_unlock_irqrestore(&priv->mcast_lock, flags); } /** * @brief find mcast node from tx packet * * @param priv A pointer to moal_private structure * @param skb A pointer to skb buffer. * * @return N/A */ t_u8 woal_find_mcast_node_tx(moal_private *priv, struct sk_buff *skb) { struct mcast_node *node = NULL; unsigned long flags; t_u8 ret = MFALSE; t_u8 ra[MLAN_MAC_ADDR_LENGTH]; ENTER(); moal_memcpy_ext(priv->phandle, ra, skb->data, MLAN_MAC_ADDR_LENGTH, sizeof(ra)); if (ra[0] & 0x01) { spin_lock_irqsave(&priv->mcast_lock, flags); list_for_each_entry (node, &priv->mcast_list, link) { if (!memcmp(node->mcast_addr, ra, ETH_ALEN)) { ret = MTRUE; break; } } spin_unlock_irqrestore(&priv->mcast_lock, flags); } LEAVE(); return ret; } /** * @brief add mcast node * * @param priv A pointer to moal_private structure * @param peer A point to peer address * * @return N/A */ t_void woal_add_mcast_node(moal_private *priv, t_u8 *mcast_addr) { struct mcast_node *node = NULL; unsigned long flags; t_u8 find_node = MFALSE; if (priv) { spin_lock_irqsave(&priv->mcast_lock, flags); list_for_each_entry (node, &priv->mcast_list, link) { if (!memcmp(node->mcast_addr, mcast_addr, ETH_ALEN)) { find_node = MTRUE; break; } } if (!find_node) { /* create new mcast node */ node = kzalloc(sizeof(struct mcast_node), GFP_ATOMIC); if (node) { moal_memcpy_ext(priv->phandle, node->mcast_addr, mcast_addr, ETH_ALEN, ETH_ALEN); INIT_LIST_HEAD(&node->link); list_add_tail(&node->link, &priv->mcast_list); PRINTM(MCMND, "Add to mcast list: node=" MACSTR "\n", MAC2STR(mcast_addr)); } } spin_unlock_irqrestore(&priv->mcast_lock, flags); } } /** * @brief This function remove mcast node * * @param priv A pointer to moal_private structure * @param mcast_addr mcast address * * @return N/A */ void woal_remove_mcast_node(moal_private *priv, t_u8 *mcast_addr) { struct mcast_node *node, *tmp = NULL; unsigned long flags; ENTER(); spin_lock_irqsave(&priv->mcast_lock, flags); list_for_each_entry_safe (node, tmp, &priv->mcast_list, link) { if (!memcmp(node->mcast_addr, mcast_addr, ETH_ALEN)) { list_del(&node->link); kfree(node); break; } } spin_unlock_irqrestore(&priv->mcast_lock, flags); LEAVE(); } #ifdef STA_CFG80211 /** * @brief This function flush tcp session queue * * @param priv A pointer to moal_private structure * * @return N/A */ void woal_flush_tdls_list(moal_private *priv) { struct tdls_peer *peer = NULL, *tmp_node; unsigned long flags; spin_lock_irqsave(&priv->tdls_lock, flags); list_for_each_entry_safe (peer, tmp_node, &priv->tdls_list, link) { list_del(&peer->link); kfree(peer); } INIT_LIST_HEAD(&priv->tdls_list); spin_unlock_irqrestore(&priv->tdls_lock, flags); priv->tdls_check_tx = MFALSE; } #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) /** * @brief check the tx packet for tdls auto set up * * @param priv A pointer to moal_private structure * @param skb A pointer to skb buffer. * * @return N/A */ static void woal_tdls_check_tx(moal_private *priv, struct sk_buff *skb) { struct tdls_peer *peer = NULL; unsigned long flags; t_u8 ra[MLAN_MAC_ADDR_LENGTH]; ENTER(); moal_memcpy_ext(priv->phandle, ra, skb->data, MLAN_MAC_ADDR_LENGTH, sizeof(ra)); spin_lock_irqsave(&priv->tdls_lock, flags); list_for_each_entry (peer, &priv->tdls_list, link) { if (!memcmp(peer->peer_addr, ra, ETH_ALEN)) { if (peer->rssi && (peer->rssi <= TDLS_RSSI_HIGH_THRESHOLD)) { if ((peer->link_status == TDLS_NOT_SETUP) && (peer->num_failure < TDLS_MAX_FAILURE_COUNT)) { peer->link_status = TDLS_SETUP_INPROGRESS; PRINTM(MMSG, "Wlan: Set up TDLS link,peer=" MACSTR " rssi=%d\n", MAC2STR(peer->peer_addr), -peer->rssi); cfg80211_tdls_oper_request( priv->netdev, peer->peer_addr, NL80211_TDLS_SETUP, 0, GFP_ATOMIC); priv->tdls_check_tx = MFALSE; } } break; } } spin_unlock_irqrestore(&priv->tdls_lock, flags); LEAVE(); } #endif #endif /** * @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; } #define TCP_SESS_AGEOUT 300 /** * @brief This function flush tcp session queue * * @param priv A pointer to moal_private structure * * @return N/A */ static void woal_ageout_tcp_sess_queue(moal_private *priv) { struct tcp_sess *tcp_sess = NULL, *tmp_node; wifi_timeval t; struct sk_buff *skb; woal_get_monotonic_time(&t); list_for_each_entry_safe (tcp_sess, tmp_node, &priv->tcp_sess_queue, link) { if (t.time_sec > (tcp_sess->update_time.time_sec + TCP_SESS_AGEOUT)) { PRINTM(MDATA, "wlan: ageout TCP seesion %p\n", tcp_sess); 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); } } } /** * @brief This function send the holding tcp ack packet * re-assoc thread. * * @param context A pointer to context * @return N/A */ static 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) { #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: 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); moal_tp_accounting_rx_param( (t_void *)priv->phandle, 8, 0); 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 */ static 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; #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: 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); moal_tp_accounting_rx_param((t_void *)priv->phandle, 8, 0); 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 */ static 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) { priv->tcp_ack_payload++; /* 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, (__force t_u32)iph->saddr, (__force t_u16)tcph->source, (__force t_u32)iph->daddr, (__force t_u16)tcph->dest); if (!tcp_session) { /* check any aging out sessions can be removed */ woal_ageout_tcp_sess_queue(priv); 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; } woal_get_monotonic_time(&tcp_session->update_time); PRINTM(MDATA, "wlan: create TCP seesion %p\n", tcp_session); tcp_session->ack_skb = pmbuf->pdesc; tcp_session->pmbuf = pmbuf; pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; tcp_session->src_ip_addr = (__force t_u32)iph->saddr; tcp_session->dst_ip_addr = (__force t_u32)iph->daddr; tcp_session->src_tcp_port = (__force t_u32)tcph->source; tcp_session->dst_tcp_port = (__force t_u32)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) { woal_get_monotonic_time(&tcp_session->update_time); 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; } woal_get_monotonic_time(&tcp_session->update_time); 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]++; if (skb->cb[0] >= priv->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; } } else if ((*((t_u8 *)tcph + 13) & 0x11) == 0x11) { /* TCP ACK + Fin */ spin_lock_irqsave(&priv->tcp_sess_lock, flags); tcp_session = woal_get_tcp_sess(priv, (__force t_u32)iph->saddr, (__force t_u16)tcph->source, (__force t_u32)iph->daddr, (__force t_u16)tcph->dest); if (tcp_session) { PRINTM(MDATA, "wlan: delete TCP seesion %p\n", tcp_session); list_del(&tcp_session->link); if (tcp_session->is_timer_set) woal_cancel_timer(&tcp_session->ack_timer); skb = (struct sk_buff *)tcp_session->ack_skb; if (skb) dev_kfree_skb_any(skb); kfree(tcp_session); } spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); } 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 */ static 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 > (priv->netdev->mtu + sizeof(struct ethhdr)))) { PRINTM(MERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, priv->netdev->mtu); dev_kfree_skb_any(skb); priv->stats.tx_dropped++; goto done; } // kernel crash with cloned skb without copy // 2 AGO case // uap0 <-->muap0 bridge if (moal_extflg_isset(priv->phandle, EXT_TX_SKB_CLONE) || skb->cloned || (skb_headroom(skb) < (MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + priv->extra_tx_head_len))) { PRINTM(MINFO, "Tx: skb cloned %d skb headroom %d tx_skb_clone=%d \n", skb->cloned, skb_headroom(skb), moal_extflg_isset(priv->phandle, EXT_TX_SKB_CLONE)); /* 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); moal_tp_accounting_rx_param((t_void *)priv->phandle, 7, 0); 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; } #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) if (priv->enable_auto_tdls && priv->tdls_check_tx) woal_tdls_check_tx(priv, skb); #endif #endif #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); moal_tp_accounting_rx_param((t_void *)priv->phandle, 8, 0); 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(); queue_work(priv->mclist_workqueue, &priv->mclist_work); 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; priv->sched_scanning = MFALSE; #ifdef STA_CFG80211 priv->roaming_enabled = MFALSE; priv->roaming_required = MFALSE; #endif 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 defined(STA_CFG80211) || defined(UAP_CFG80211) #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) woal_init_wifi_hal(priv); #endif #endif } #endif /* STA_SUPPORT */ #ifdef UAP_SUPPORT if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { priv->bss_started = MFALSE; priv->uap_host_based = MFALSE; priv->skip_cac = 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 defined(STA_CFG80211) || defined(UAP_CFG80211) #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) woal_init_wifi_hal(priv); #endif #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; priv->mgmt_subtype_mask = 0; priv->cfg_disconnect = MFALSE; priv->cfg_connect = MFALSE; #endif #ifdef STA_SUPPORT #ifdef STA_CFG80211 priv->pmk_saved = MFALSE; memset(&priv->pmk, 0, sizeof(mlan_pmk_t)); #endif #endif priv->enable_tcp_ack_enh = MTRUE; priv->tcp_ack_drop_cnt = 0; priv->tcp_ack_cnt = 0; priv->tcp_ack_payload = 0; priv->tcp_ack_max_hold = TCP_ACK_MAX_HOLD; priv->enable_auto_tdls = MFALSE; priv->tdls_check_tx = MFALSE; 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 && priv->bss_type != MLAN_BSS_TYPE_DFS) { 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)); } /* ZeroDFS interface doesn't need to set mac address to fw */ if (priv->bss_type != MLAN_BSS_TYPE_DFS) woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) eth_hw_addr_set(priv->netdev, priv->current_addr); #else moal_memcpy_ext(priv->phandle, priv->netdev->dev_addr, priv->current_addr, ETH_ALEN, ETH_ALEN); #endif #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) priv->host_mlme = 0; priv->auth_flag = 0; priv->auth_alg = 0xFFFF; #endif #ifdef UAP_SUPPORT priv->target_chan = 0; priv->backup_chan = 0; priv->user_cac_period_msec = 0; priv->chan_under_nop = MFALSE; #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 defined(STA_CFG80211) && defined(UAP_CFG80211) /* Unregister and detach connected radiotap net device */ if (handle->mon_if) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) woal_set_net_monitor(handle->mon_if->priv, wait_option, MFALSE, 0, NULL); #endif netif_device_detach(handle->mon_if->mon_ndev); if (handle->mon_if->mon_ndev->reg_state == NETREG_REGISTERED) unregister_netdev(handle->mon_if->mon_ndev); handle->mon_if = NULL; } #endif if (handle->rf_test_mode) woal_process_rf_test_mode(handle, MFG_CMD_UNSET_TEST_MODE); #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) #endif #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 */ if (handle->is_fw_dump_timer_set) { woal_cancel_timer(&handle->fw_dump_timer); handle->is_fw_dump_timer_set = MFALSE; } #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 SD8801 case CARD_TYPE_SD8801: phandle->card_info = &card_info_SD8801; break; #endif #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 SDNW62X case CARD_TYPE_SDNW62X: phandle->card_info = &card_info_SDNW62X; break; #endif #ifdef SD9177 case CARD_TYPE_SD9177: phandle->card_info = &card_info_SD9177; phandle->event_fw_dump = MFALSE; 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 PCIENW62X case CARD_TYPE_PCIENW62X: phandle->card_info = &card_info_PCIENW62X; break; #endif #ifdef PCIE9098 case CARD_TYPE_PCIE9098: phandle->card_info = &card_info_PCIE9098; phandle->event_fw_dump = MTRUE; break; #endif #ifdef USB8801 case CARD_TYPE_USB8801: phandle->card_info = &card_info_USB8801; 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 USBNW62X case CARD_TYPE_USBNW62X: phandle->card_info = &card_info_USBNW62X; 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 = NULL; 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(); ssid_bssid = kmalloc(sizeof(mlan_ssid_bssid), GFP_KERNEL); if (!ssid_bssid) { LEAVE(); return 0; } 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); kfree(ssid_bssid); 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 */ /** * @brief This function triggers process hang * re-assoc thread. * * @param context A pointer to context * @return N/A */ void woal_fw_dump_timer_func(void *context) { moal_handle *handle = (moal_handle *)context; ENTER(); PRINTM(MMSG, "fw_dump_timer fired.\n"); handle->is_fw_dump_timer_set = MFALSE; woal_process_hang(handle); LEAVE(); return; } #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 = NULL; 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(); assoc_rsp = kmalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_KERNEL); if (!assoc_rsp) { LEAVE(); return; } 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; } } } kfree(assoc_rsp); 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 #ifdef STA_CFG80211 t_u16 reason_code = 0; #endif ENTER(); priv->media_connected = MFALSE; #ifdef STA_CFG80211 if (!disconnect_reason) reason_code = MLAN_REASON_DEAUTH_LEAVING; else reason_code = disconnect_reason; #endif 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) woal_flush_tdls_list(priv); #endif woal_flush_mcast_list(priv); #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); memcpy(event_buf, CUS_EVT_AP_CONNECTED, MIN((int)(sizeof(event_buf) - 1), custom_len)); woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN); LEAVE(); } #endif /* STA_SUPPORT */ #define OFFSET_SEQNUM 4 #define OFFSET_TYPE 8 #define DUMP_TYPE_ENDE 2 /** * @brief This function stores the FW dumps received from events * * @param phandle A pointer to moal_handle * @param pmevent A pointer to mlan_event structure * * @return N/A */ t_void woal_store_firmware_dump(moal_handle *phandle, mlan_event *pmevent) { int ret = 0; t_u16 seqnum; t_u16 type = 0; t_u8 *pos; ENTER(); if (!phandle || !pmevent) { PRINTM(MERROR, "Could not dump firmware info\n"); LEAVE(); return; } 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; if (phandle->fw_dump == MFALSE) { PRINTM(MMSG, "=====FW trigger dump====\n"); phandle->fw_dump = MTRUE; phandle->is_fw_dump_timer_set = MTRUE; woal_mod_timer(&phandle->fw_dump_timer, MOAL_TIMER_5S); } if (!phandle->fw_dump_buf) { ret = moal_vmalloc(phandle, FW_DUMP_INFO_LEN, &phandle->fw_dump_buf); if (ret != MLAN_STATUS_SUCCESS || !phandle->fw_dump_buf) { PRINTM(MERROR, "Failed to vmalloc fw dump buffer\n"); LEAVE(); return; } } else { memset(phandle->fw_dump_buf, 0x00, FW_DUMP_INFO_LEN); } phandle->fw_dump_len = 0; PRINTM(MMSG, "==== Start Receive FW dump event ====\n"); } else { if (!phandle->fw_dump_buf || !phandle->fw_dump_len) { PRINTM(MERROR, "Error! Fw dump buffer is null or dump len is zero\n"); LEAVE(); return; } } pos = phandle->fw_dump_buf + phandle->fw_dump_len; moal_memcpy_ext(phandle, pos, pmevent->event_buf + OFFSET_SEQNUM, pmevent->event_len - OFFSET_SEQNUM, FW_DUMP_INFO_LEN - phandle->fw_dump_len); phandle->fw_dump_len += pmevent->event_len - OFFSET_SEQNUM; PRINTM(MINFO, "fw dump event: evt_len=%d toal_len=%ld\n", pmevent->event_len, (long int)phandle->fw_dump_len); if (type == DUMP_TYPE_ENDE) { PRINTM(MMSG, "==== FW DUMP END: %ld bytes ====\n", (long int)phandle->fw_dump_len); woal_append_end_block(phandle); phandle->fw_dump = MFALSE; woal_send_fw_dump_complete_event( woal_get_priv(phandle, MLAN_BSS_ROLE_ANY)); mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); if (phandle->is_fw_dump_timer_set) { woal_cancel_timer(&phandle->fw_dump_timer); phandle->is_fw_dump_timer_set = MFALSE; } #ifdef USB if (IS_USB(phandle->card_type)) phandle->driver_status = MTRUE; #endif woal_process_hang(phandle); } 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 < (int)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 < (int)(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_data_port_mask=0x%x\n", info->mp_data_port_mask); ptr += sprintf( ptr, "last_recv_rd_bitmap=0x%x mp_invalid_update=%d\n", info->last_recv_rd_bitmap, 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 & (info->txrx_bd_size - 1)); ptr += sprintf(ptr, "TxRx BD size:%d\n", info->txrx_bd_size); } #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, "bypass_pkt_count=%d\n", info->bypass_pkt_count); ptr += sprintf(ptr, "data_sent=%d cmd_sent=%d\n", info->data_sent, info->cmd_sent); ptr += sprintf(ptr, "data_sent_cnt=%u\n", info->data_sent_cnt); 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; } /** * @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++) { if (!phandle->priv[i]) continue; ptr += sprintf(ptr, "<--moal_private(%d) %s-->\n", i, phandle->priv[i]->netdev->name); 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; } /** * @brief This function dump drv info to file * * @param phandle A pointer to moal_handle * @param dump_len A point to hold the len of drv info memory * * @return A pointer to drv_info memory */ t_u8 *woal_dump_drv_info(moal_handle *phandle, t_u32 *dump_len) { t_u8 *drv_buf = NULL; t_u32 len = 0; t_u32 total_len = 0; t_u32 drv_info_size = DRV_INFO_SIZE; int ret; if (phandle->priv_num > 3) drv_info_size += (phandle->priv_num - 3) * DRV_INFO_PER_INTF; PRINTM(MERROR, "=== START DRIVER INFO DUMP==="); 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_get_priv(phandle, MLAN_BSS_ROLE_ANY), 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_get_priv(phandle, MLAN_BSS_ROLE_ANY), drv_buf + total_len); total_len += len; PRINTM(MERROR, "Drv info total bytes = %ld (0x%lx)\n", (long int)total_len, (long unsigned int)total_len); PRINTM(MERROR, "=== DRIVER INFO DUMP END==="); *dump_len = total_len; done: return drv_buf; } /** * @brief This function adds header and copy the src data to buf * * @param phandle A pointer to moal_handle * @param src A ponter to source buffer * @param len Length of raw data * @param type Dump type * * return Total len of buf */ int woal_save_dump_info_to_buf(moal_handle *phandle, t_u8 *src, t_u32 len, t_u32 type) { mem_dump_header header; t_u32 left_len = 0; t_u32 len_to_copy = 0; int total_len = 0; t_u8 *dest = NULL; int count = 0; int pad = 0; dest = phandle->fw_dump_buf + phandle->fw_dump_len; left_len = len; while (left_len) { header.type = type; if (left_len < 1024) len_to_copy = left_len; else len_to_copy = 1024; header.len = (t_u16)(len_to_copy + sizeof(t_u32)); header.start_addr = count * 1024; moal_memcpy_ext(phandle, dest, &header, sizeof(mem_dump_header), FW_DUMP_INFO_LEN - phandle->fw_dump_len); dest += sizeof(mem_dump_header); moal_memcpy_ext(phandle, dest, src, len_to_copy, FW_DUMP_INFO_LEN - phandle->fw_dump_len - sizeof(mem_dump_header)); dest += len_to_copy; src += len_to_copy; left_len -= len_to_copy; // 8 bytes align pad = (header.len & 7) ? (8 - (header.len & 7)) : 0; dest += pad; total_len += pad + len_to_copy + sizeof(mem_dump_header); count++; } phandle->fw_dump_len += total_len; PRINTM(MMSG, "type=%d, len=%d block=%d total=%d\n", type, len, count, total_len); return total_len; } /** * @brief This function append end block to dump file * * @param phandle A pointer to moal_handle * * return N/A */ void woal_append_end_block(moal_handle *phandle) { mem_dump_header header; t_u8 *pos = phandle->fw_dump_buf + phandle->fw_dump_len; ENTER(); memset(&header, 0, sizeof(header)); header.type = FW_DUMP_TYPE_ENDED; header.len = 0; moal_memcpy_ext(phandle, pos, &header, sizeof(mem_dump_header), FW_DUMP_INFO_LEN - phandle->fw_dump_len); phandle->fw_dump_len += sizeof(mem_dump_header); PRINTM(MMSG, "fw dump total length is %ld\n", (long int)phandle->fw_dump_len); LEAVE(); return; } /** * @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 < (int)(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))) { if (!phandle->event_fw_dump) 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))) { if (!phandle->event_fw_dump) phandle->ops.reg_dbg(phandle); } } else { if (drvdbg & (MREG_D | MFW_D)) { if (!phandle->event_fw_dump) { 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[128]; 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; } handle = priv->phandle; memset(country_name, 0, sizeof(country_name)); if (handle->params.hw_name) sprintf(country_name, "%s_txpower_XX.bin", handle->params.hw_name); else memcpy(country_name, "txpower_XX.bin", strlen("txpower_XX.bin")); #if defined(STA_CFG80211) || defined(UAP_CFG80211) #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE) { memset(country_name, 0, sizeof(country_name)); if (handle->params.hw_name) sprintf(country_name, "%s_rgpower_XX.bin", handle->params.hw_name); else memcpy(country_name, "rgpower_XX.bin", strlen("rgpower_XX.bin")); } #endif #endif /* Replace XX with ISO 3166-1 alpha-2 country code */ memcpy(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 { memcpy(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 ((handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE) && (ret == MLAN_STATUS_FILE_ERR)) { memset(country_name, 0, sizeof(country_name)); if (handle->params.hw_name) sprintf(country_name, "%s_rgpower_WW.bin", handle->params.hw_name); else memcpy(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)); 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); if (!ret) { handle->country_code[0] = '0'; handle->country_code[1] = '0'; } } #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 */ static 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 set multicast_list * * @param work A pointer to work_struct * * @return N/A */ t_void woal_mclist_work_queue(struct work_struct *work) { moal_private *priv = container_of(work, moal_private, mclist_work); woal_request_set_multicast_list(priv, priv->netdev); } /** * @brief This workqueue function handles woal scan deferred work * * @param work A pointer to work_struct * * @return N/A */ t_void woal_scan_deferred_work_queue(struct work_struct *work) { #ifdef STA_CFG80211 struct delayed_work *delayed_work = container_of(work, struct delayed_work, work); moal_private *priv = container_of(delayed_work, moal_private, scan_deferred_work); unsigned long flags; #endif ENTER(); PRINTM(MINFO, "Reporting scan results from scan deferred wq\n"); #ifdef STA_CFG80211 if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) { if (priv->phandle->scan_request) { woal_inform_bss_from_scan_result(priv, NULL, MOAL_NO_WAIT); spin_lock_irqsave(&priv->phandle->scan_req_lock, flags); woal_cfg80211_scan_done(priv->phandle->scan_request, MFALSE); priv->phandle->scan_request = NULL; spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags); } } #endif LEAVE(); } /** * @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_RX_MGMT_PKT: #if defined(UAP_CFG80211) || defined(STA_CFG80211) #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) priv = evt->priv; mutex_lock(&priv->wdev->mtx); cfg80211_rx_mlme_mgmt(priv->netdev, evt->evt.event_buf, evt->evt.event_len); mutex_unlock(&priv->wdev->mtx); #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_info); break; #endif #endif case WOAL_EVENT_CHAN_RPT: woal_process_chan_event((moal_private *)evt->priv, WOAL_EVENT_CHAN_RPT, evt->radar_info.channel, evt->radar_info.radar); break; case WOAL_EVENT_RADAR: woal_process_chan_event((moal_private *)evt->priv, WOAL_EVENT_RADAR, evt->radar_info.channel, evt->radar_info.radar); break; #ifdef UAP_CFG80211 #if KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE case WOAL_EVENT_CANCEL_CHANRPT: woal_process_cancel_chanrpt_event( (moal_private *)evt->priv); 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; } #if defined(STA_CFG80211) || defined(UAP_CFG80211) if (handle->cfg80211_suspend == MTRUE) { LEAVE(); return; } #endif 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(); } #ifdef IMX_SUPPORT /** * @brief This function allocates the interrupt line to wakeup * the host, and initializes the device for wakeup * * @param handle A pointer to moal_handle structure * * @return N/A */ void woal_regist_oob_wakeup_irq(moal_handle *handle) { int ret; struct device *dev = handle->hotplug_device; struct device_node *node; ENTER(); node = of_find_compatible_node(NULL, NULL, "nxp,wifi-wake-host"); if (!node) goto err_exit; handle->irq_oob_wakeup = irq_of_parse_and_map(node, 0); if (!handle->irq_oob_wakeup) { dev_dbg(dev, "fail to parse irq_oob_wakeup from device tree\n"); goto err_exit; } ret = devm_request_irq(dev, handle->irq_oob_wakeup, woal_oob_wakeup_irq_handler, IRQF_TRIGGER_LOW | IRQF_SHARED, "wifi_oob_wakeup", handle); if (ret) { dev_err(dev, "Failed to request irq_oob_wakeup %d (%d)\n", handle->irq_oob_wakeup, ret); goto err_exit; } disable_irq(handle->irq_oob_wakeup); LEAVE(); return; err_exit: handle->irq_oob_wakeup = -1; } /** * @brief This function frees the wakeup interrupt line * * @param handle A pointer to moal_handle structure * * @return N/A */ void woal_unregist_oob_wakeup_irq(moal_handle *handle) { struct device *dev = handle->hotplug_device; ENTER(); if (handle->irq_oob_wakeup >= 0) { devm_free_irq(dev, handle->irq_oob_wakeup, handle); } LEAVE(); } /** * @brief This function disables power management wakeup * * @param handle A pointer to moal_handle structure * * @return N/A */ void woal_disable_oob_wakeup_irq(moal_handle *handle) { ENTER(); if (handle->irq_oob_wakeup >= 0) { if (handle->wake_by_wifi) disable_irq_wake(handle->irq_oob_wakeup); else { disable_irq_wake(handle->irq_oob_wakeup); disable_irq(handle->irq_oob_wakeup); } } LEAVE(); } /** * @brief This function enables power management wakeup * * @param handle A pointer to moal_handle structure * * @return N/A */ void woal_enable_oob_wakeup_irq(moal_handle *handle) { ENTER(); /* Enable platform specific wakeup interrupt */ if (handle->irq_oob_wakeup >= 0) { handle->wake_by_wifi = false; enable_irq(handle->irq_oob_wakeup); enable_irq_wake(handle->irq_oob_wakeup); } LEAVE(); } /** * @brief This function will be called when the wakeup IRQ occurs * * @param irq Wakeup interrupt line * @param priv A void pointer to store moal_handle structure pointer * * @return Returns status of interrupt handler. */ irqreturn_t woal_oob_wakeup_irq_handler(int irq, void *priv) { moal_handle *handle = priv; struct device *dev = handle->hotplug_device; ENTER(); dev_dbg(dev, "%s: OOB wakeup by wifi", __func__); handle->wake_by_wifi = true; disable_irq_nosync(irq); /* Notify PM core we are wakeup source */ pm_wakeup_event(dev, 0); pm_system_wakeup(); LEAVE(); return IRQ_HANDLED; } #endif /* IMX_SUPPORT */ /** * @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); #ifdef IMX_SUPPORT #ifdef SDIO if (IS_SD(handle->card_type)) { moal_extflg_set(handle, EXT_TX_WORK); moal_extflg_set(handle, EXT_TX_SKB_CLONE); } #endif #endif #ifdef DEBUG_LEVEL1 drvdbg = handle->params.drvdbg; #endif 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(4, 0, 0) MLAN_INIT_WORK(&handle->regulatory_work, woal_regulatory_work_queue); #endif #endif #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; } device_init_wakeup(dev, true); #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; device_init_wakeup(handle->hotplug_device, false); #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; woal_flush_workqueue(handle); 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)); } unregister_inetaddr_notifier(&handle->woal_notifier); #if IS_ENABLED(CONFIG_IPV6) unregister_inet6addr_notifier(&handle->woal_inet6_notifier); #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 #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); #if defined(STA_CFG80211) && defined(UAP_CFG80211) /* Unregister and detach connected radiotap net device */ if (handle->mon_if) { netif_device_detach(handle->mon_if->mon_ndev); if (handle->mon_if->mon_ndev->reg_state == NETREG_REGISTERED) unregister_netdev(handle->mon_if->mon_ndev); handle->mon_if = NULL; } #endif 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 */ PRINTM(MINFO, "Free FW dump timer\n"); if (handle->is_fw_dump_timer_set) { woal_cancel_timer(&handle->fw_dump_timer); handle->is_fw_dump_timer_set = MFALSE; } #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)); } unregister_inetaddr_notifier(&handle->woal_notifier); #if IS_ENABLED(CONFIG_IPV6) unregister_inet6addr_notifier(&handle->woal_inet6_notifier); #endif /* 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 * @param mode FW_RELOAD_SDIO_INBAND_RESET or FW_RELOAD_SDIO_HW_RESET * * @return 0--success, otherwise failure */ static int woal_reset_and_reload_fw(moal_handle *handle, t_u8 mode) { 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 (mode == FW_RELOAD_SDIO_HW_RESET) { PRINTM(MCMND, "woal_sdio_reset_fw...\n"); woal_sdio_reset_hw(handle); goto reload_fw; } if (!IS_SD9098(handle->card_type) && !IS_SD9097(handle->card_type) && !IS_SDNW62X(handle->card_type) && !IS_SD9177(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) || defined(SDNW62X) || defined(SD9177) if (IS_SD9098(handle->card_type) || IS_SD9097(handle->card_type) || IS_SDNW62X(handle->card_type) || IS_SD9177(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) && !IS_SDNW62X(handle->card_type) && !IS_SD9177(handle->card_type)) mlan_pm_wakeup_card(handle->pmlan_adapter, MFALSE); reload_fw: /* 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; } /** * @brief This function handle the pre_reset * * @param handle A pointer to moal_handle structure * * @return NULL; */ static void woal_pre_reset(moal_handle *handle) { int intf_num; t_u8 driver_status = handle->driver_status; t_u8 i; moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_STA); mlan_debug_info *info = &(handle->debug_info); int ioctl_pending = 0; ENTER(); if (!driver_status && priv) woal_cancel_scan(priv, MOAL_IOCTL_WAIT); handle->driver_status = MTRUE; if (!driver_status) woal_sched_timeout_uninterruptible(MOAL_TIMER_1S); // wait for IOCTL return if (!driver_status && priv) { for (i = 0; i < 5; i++) { woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info); ioctl_pending = atomic_read(&handle->ioctl_pending); if (!info->pending_cmd && !ioctl_pending) { PRINTM(MCMND, "fw_reload: No pending command and IOCTL\n"); break; } woal_sched_timeout_uninterruptible(MOAL_TIMER_1S); } } /** detach network interface */ for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { if (handle->priv[intf_num]) { woal_stop_queue(handle->priv[intf_num]->netdev); netif_device_detach(handle->priv[intf_num]->netdev); } } /** mask host interrupt from firmware */ mlan_disable_host_int(handle->pmlan_adapter); /** cancel all pending commands */ mlan_ioctl(handle->pmlan_adapter, NULL); woal_flush_workqueue(handle); handle->fw_reload = MTRUE; woal_update_firmware_name(handle); #ifdef USB if (IS_USB(handle->card_type)) woal_kill_urbs(handle); #endif LEAVE(); } /** * @brief This function handle pose_reset * * @param handle A pointer to moal_handle structure * * @return NULL; */ static void woal_post_reset(moal_handle *handle) { mlan_ioctl_req *req = NULL; mlan_ds_misc_cfg *misc = NULL; int intf_num; #if defined(STA_CFG80211) || defined(UAP_CFG80211) moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); t_u8 country_code[COUNTRY_CODE_LEN]; #endif #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; /* 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_TIMEOUT)) { kfree(req); goto done; } kfree(req); } #ifdef DEBUG_LEVEL1 drvdbg = handle->params.drvdbg; #endif handle->driver_status = MFALSE; 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++) { if (handle->priv[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++) { if (handle->priv[intf_num]) { netif_device_attach(handle->priv[intf_num]->netdev); woal_start_queue(handle->priv[intf_num]->netdev); } } #if defined(STA_CFG80211) || defined(UAP_CFG80211) if (handle->country_code[0] && handle->country_code[1]) { memset(country_code, 0, sizeof(country_code)); if (MTRUE == is_cfg80211_special_region_code(handle->country_code)) { country_code[0] = 'W'; country_code[1] = 'W'; } else { country_code[0] = handle->country_code[0]; country_code[1] = handle->country_code[1]; } if (handle->params.cntry_txpwr && priv) woal_request_country_power_table(priv, country_code); #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) if (handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE) queue_work(handle->evt_workqueue, &handle->regulatory_work); #endif } #endif 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; } /** * @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; } } 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 || mode == FW_RELOAD_SDIO_HW_RESET) && IS_SD(handle->card_type)) { ret = woal_reset_and_reload_fw(handle, mode); 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; } /** * @brief This function register to bus driver * * @param work a pointer to struct work_struct * * @return N/A */ static void woal_bus_register(struct work_struct *work) { mlan_status ret = MLAN_STATUS_SUCCESS; PRINTM(MMSG, "wlan: Register to Bus Driver...\n"); #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 PRINTM(MMSG, "wlan: Register to Bus Driver Done\n"); out: return; } /** * @brief This function unregister from bus driver * * * @return N/A */ 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); if (reg_work) { /* 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 */ register_workqueue = create_workqueue("MOAL_REGISTER_WORKQ"); #else #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) register_workqueue = alloc_workqueue( "MOAL_REGISTER_WORK_QUEUE", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); #else register_workqueue = create_workqueue("MOAL_REGISTER_WORK_QUEUE"); #endif #endif MLAN_INIT_WORK(®ister_work, woal_bus_register); queue_work(register_workqueue, ®ister_work); } else { woal_bus_register(NULL); } PRINTM(MMSG, "wlan: Driver loaded successfully\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); #if defined(STA_CFG80211) && defined(UAP_CFG80211) /* Unregister all connected radiotap net devices */ if (handle->mon_if) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) woal_set_net_monitor(handle->mon_if->priv, MOAL_IOCTL_WAIT, MFALSE, 0, NULL); if (handle->ioctl_timeout) { woal_ioctl_timeout(handle); goto exit; } #endif netif_device_detach(handle->mon_if->mon_ndev); if (handle->mon_if->mon_ndev->reg_state == NETREG_REGISTERED) unregister_netdev(handle->mon_if->mon_ndev); handle->mon_if = NULL; } #endif for (i = 0; i < handle->priv_num; i++) { /** cancel dfs monitor on deinit */ if (handle->priv[i] && handle->priv[i]->bss_type == MLAN_BSS_TYPE_DFS) { woal_11h_cancel_chan_report_ioctl( handle->priv[i], MOAL_IOCTL_WAIT); continue; } #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; } if (reg_work && register_workqueue) { flush_workqueue(register_workqueue); destroy_workqueue(register_workqueue); register_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_param(reg_work, int, 0); MODULE_PARM_DESC( reg_work, "0: disable register work_queue; 1: enable register work_queue"); MODULE_DESCRIPTION("M-WLAN Driver"); MODULE_AUTHOR("NXP"); MODULE_VERSION(MLAN_RELEASE_VERSION); MODULE_LICENSE("GPL");