mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2025-01-15 16:25:35 +00:00
280b53192f
Port the driver to support L5.4 kernel. Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
8703 lines
228 KiB
C
Executable file
8703 lines
228 KiB
C
Executable file
/** @file moal_main.c
|
|
*
|
|
* @brief This file contains the major functions in WLAN
|
|
* driver.
|
|
*
|
|
* Copyright (C) 2008-2019, Marvell International Ltd.
|
|
*
|
|
* This software file (the "File") is distributed by Marvell International
|
|
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
|
|
* (the "License"). You may use, redistribute and/or modify this File in
|
|
* accordance with the terms and conditions of the License, a copy of which
|
|
* is available by writing to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
|
|
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
|
|
*
|
|
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
|
|
* this warranty disclaimer.
|
|
*
|
|
*/
|
|
|
|
/********************************************************
|
|
Change log:
|
|
10/21/2008: initial version
|
|
********************************************************/
|
|
|
|
#include "moal_main.h"
|
|
#include "moal_pcie.h"
|
|
#ifdef UAP_SUPPORT
|
|
#include "moal_uap.h"
|
|
#endif
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
#include "moal_cfg80211.h"
|
|
#include "moal_cfgvendor.h"
|
|
#endif
|
|
#ifdef STA_CFG80211
|
|
#ifdef STA_SUPPORT
|
|
#include "moal_sta_cfg80211.h"
|
|
#endif
|
|
#endif
|
|
#ifdef UAP_CFG80211
|
|
#ifdef UAP_SUPPORT
|
|
#include "moal_uap_cfg80211.h"
|
|
#endif
|
|
#endif
|
|
#include "moal_eth_ioctl.h"
|
|
|
|
#include <linux/if_ether.h>
|
|
#include <linux/in.h>
|
|
#include <linux/tcp.h>
|
|
#include <net/tcp.h>
|
|
#include <net/dsfield.h>
|
|
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of.h>
|
|
#endif
|
|
|
|
/********************************************************
|
|
Local Variables
|
|
********************************************************/
|
|
|
|
#define KERN_VERSION "4X"
|
|
|
|
/** Driver version */
|
|
char driver_version[] =
|
|
"PCIE8997-%s-C" KERN_VERSION "16" MLAN_RELEASE_VERSION
|
|
"-GPL" "-(" "FP" FPNUM ")"
|
|
#ifdef DEBUG_LEVEL2
|
|
"-dbg"
|
|
#endif
|
|
" ";
|
|
|
|
/** Firmware name */
|
|
char *fw_name;
|
|
int req_fw_nowait;
|
|
int fw_reload;
|
|
|
|
/** MAC address */
|
|
char *mac_addr;
|
|
|
|
#ifdef MFG_CMD_SUPPORT
|
|
/** Mfg mode */
|
|
int mfg_mode;
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
/** Region alpha2 string */
|
|
extern char *reg_alpha2;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
extern int country_ie_ignore;
|
|
extern int beacon_hints;
|
|
#endif
|
|
#endif
|
|
extern int cfg80211_drcs;
|
|
#endif
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
extern int host_mlme;
|
|
#endif
|
|
#endif
|
|
|
|
int drcs_chantime_mode = 0;
|
|
|
|
/** Auto deep sleep */
|
|
int auto_ds;
|
|
|
|
/** IEEE PS mode */
|
|
int ps_mode;
|
|
|
|
/** Max Tx buffer size */
|
|
int max_tx_buf;
|
|
|
|
#ifdef STA_SUPPORT
|
|
/** Max STA interfaces */
|
|
int max_sta_bss = DEF_STA_BSS;
|
|
/** STA interface name */
|
|
char *sta_name;
|
|
#endif
|
|
|
|
#ifdef UAP_SUPPORT
|
|
/** Max uAP interfaces */
|
|
int max_uap_bss = DEF_UAP_BSS;
|
|
/** uAP interface name */
|
|
char *uap_name;
|
|
/** Max uAP station number */
|
|
int uap_max_sta = 0;
|
|
#endif
|
|
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
/** Max WIFIDIRECT interfaces */
|
|
int max_wfd_bss = DEF_WIFIDIRECT_BSS;
|
|
/** WIFIDIRECT interface name */
|
|
char *wfd_name;
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
/** max VIRTUAL bss */
|
|
int max_vir_bss = DEF_VIRTUAL_BSS;
|
|
#endif
|
|
#endif
|
|
|
|
/** Max NAN interfaces */
|
|
int max_nan_bss = DEF_NAN_BSS;
|
|
/** NAN interface name */
|
|
char *nan_name;
|
|
|
|
#if defined(STA_SUPPORT)
|
|
/** 802.11d configuration */
|
|
int cfg_11d;
|
|
#endif
|
|
|
|
/** fw serial download check */
|
|
int fw_serial = 1;
|
|
|
|
/** napi support*/
|
|
int napi;
|
|
|
|
/** DPD data config file */
|
|
char *dpd_data_cfg;
|
|
|
|
/** CAL data config file */
|
|
char *cal_data_cfg;
|
|
/** Init config file (MAC address, register etc.) */
|
|
char *init_cfg;
|
|
|
|
/** Set configuration data of Tx power limitation */
|
|
char *txpwrlimit_cfg;
|
|
/** Set configuration data of Tx power limitatio */
|
|
char *country_txpwrlimit;
|
|
/** Allow setting tx power table of country */
|
|
int cntry_txpwr = 0;
|
|
|
|
/** Init hostcmd file */
|
|
char *init_hostcmd_cfg;
|
|
|
|
#if defined(STA_WEXT) || defined(UAP_WEXT)
|
|
/** CFG80211 and WEXT mode */
|
|
int cfg80211_wext = STA_WEXT_MASK | UAP_WEXT_MASK;
|
|
#else
|
|
/** CFG80211 mode */
|
|
int cfg80211_wext = STA_CFG80211_MASK | UAP_CFG80211_MASK;
|
|
#endif
|
|
|
|
int fw_region = 1;
|
|
|
|
/** Work queue priority */
|
|
int wq_sched_prio;
|
|
/** Work queue scheduling policy */
|
|
int wq_sched_policy = SCHED_NORMAL;
|
|
/** rx_work flag */
|
|
int rx_work;
|
|
|
|
/* Enable/disable Message Signaled Interrupt (MSI) */
|
|
int pcie_int_mode = PCIE_INT_MODE_MSI;
|
|
|
|
int hw_test;
|
|
|
|
#ifdef CONFIG_OF
|
|
int dts_enable = 1;
|
|
#endif
|
|
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
int p2p_enh;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
int dfs_offload = 0;
|
|
#endif
|
|
|
|
int roamoffload_in_hs = 0;
|
|
|
|
#ifdef ANDROID_KERNEL
|
|
int wakelock_timeout = WAKE_LOCK_TIMEOUT;
|
|
#endif
|
|
|
|
/** woal_callbacks */
|
|
static mlan_callbacks woal_callbacks = {
|
|
.moal_get_fw_data = moal_get_fw_data,
|
|
.moal_get_hw_spec_complete = moal_get_hw_spec_complete,
|
|
.moal_init_fw_complete = moal_init_fw_complete,
|
|
.moal_shutdown_fw_complete = moal_shutdown_fw_complete,
|
|
.moal_send_packet_complete = moal_send_packet_complete,
|
|
.moal_recv_packet = moal_recv_packet,
|
|
.moal_recv_event = moal_recv_event,
|
|
.moal_ioctl_complete = moal_ioctl_complete,
|
|
.moal_alloc_mlan_buffer = moal_alloc_mlan_buffer,
|
|
.moal_free_mlan_buffer = moal_free_mlan_buffer,
|
|
|
|
.moal_write_reg = moal_write_reg,
|
|
.moal_read_reg = moal_read_reg,
|
|
.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,
|
|
.moal_malloc_consistent = moal_malloc_consistent,
|
|
.moal_mfree_consistent = moal_mfree_consistent,
|
|
.moal_map_memory = moal_map_memory,
|
|
.moal_unmap_memory = moal_unmap_memory,
|
|
.moal_memset = moal_memset,
|
|
.moal_memcpy = moal_memcpy,
|
|
.moal_memmove = moal_memmove,
|
|
.moal_memcmp = moal_memcmp,
|
|
.moal_udelay = moal_udelay,
|
|
.moal_get_system_time = moal_get_system_time,
|
|
.moal_get_boot_ktime = moal_get_boot_ktime,
|
|
.moal_usleep = moal_usleep,
|
|
.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_get_host_time_ns = moal_get_host_time_ns,
|
|
.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
|
|
};
|
|
|
|
#if defined(STA_SUPPORT) && defined(UAP_SUPPORT)
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
int drv_mode = (DRV_MODE_STA | DRV_MODE_UAP | DRV_MODE_WIFIDIRECT);
|
|
#else
|
|
int drv_mode = (DRV_MODE_STA | DRV_MODE_UAP);
|
|
#endif
|
|
#else
|
|
#ifdef STA_SUPPORT
|
|
int drv_mode = DRV_MODE_STA;
|
|
#else
|
|
int drv_mode = DRV_MODE_UAP;
|
|
#endif /* STA_SUPPORT */
|
|
#endif /* STA_SUPPORT & UAP_SUPPORT */
|
|
|
|
int gtk_rekey_offload = GTK_REKEY_OFFLOAD_DISABLE;
|
|
|
|
int pmic = 0;
|
|
|
|
t_u32 uap_oper_ctrl = 0;
|
|
|
|
int hs_wake_interval = 400;
|
|
int indication_gpio = 0xff;
|
|
int hs_mimo_switch = 0;
|
|
|
|
int indrstcfg = 0xffffffff;
|
|
|
|
/** all the feature are enabled */
|
|
#define DEFAULT_DEV_CAP_MASK 0xffffffff
|
|
t_u32 dev_cap_mask = DEFAULT_DEV_CAP_MASK;
|
|
/********************************************************
|
|
Global Variables
|
|
********************************************************/
|
|
|
|
/** 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];
|
|
|
|
/** The global variable of scan beacon buffer **/
|
|
int fixed_beacon_buffer = 0;
|
|
/** the pointer of new fwdump fname for each dump**/
|
|
char *fwdump_fname = NULL;
|
|
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
int GoAgeoutTime = 0;
|
|
#endif
|
|
|
|
t_u16 multi_dtim = 0;
|
|
|
|
t_u16 inact_tmo = 0;
|
|
|
|
#ifdef DEBUG_LEVEL1
|
|
#ifdef DEBUG_LEVEL2
|
|
#define DEFAULT_DEBUG_MASK (0xffffffff)
|
|
#else
|
|
#define DEFAULT_DEBUG_MASK (MMSG | MFATAL | MERROR)
|
|
#endif /* DEBUG_LEVEL2 */
|
|
t_u32 drvdbg = DEFAULT_DEBUG_MASK;
|
|
|
|
#endif /* DEBUG_LEVEL1 */
|
|
|
|
int woal_open(struct net_device *dev);
|
|
int woal_close(struct net_device *dev);
|
|
int woal_set_mac_address(struct net_device *dev, void *addr);
|
|
void woal_tx_timeout(struct net_device *dev);
|
|
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, 3, 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
|
|
|
|
mlan_debug_info info;
|
|
|
|
static moal_handle *reset_handle;
|
|
/** Hang workqueue */
|
|
static struct workqueue_struct *hang_workqueue;
|
|
/** Hang work */
|
|
static struct work_struct hang_work;
|
|
|
|
/**
|
|
* @brief This function process FW hang
|
|
*
|
|
* @param handle Pointer to structure moal_handle
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void
|
|
woal_hang_work_queue(struct work_struct *work)
|
|
{
|
|
int i;
|
|
ENTER();
|
|
if (!reset_handle) {
|
|
LEAVE();
|
|
return;
|
|
}
|
|
for (i = 0; i < reset_handle->priv_num; i++) {
|
|
if (reset_handle->priv[i] && reset_handle->priv[i]->netdev) {
|
|
PRINTM(MMSG, "Close netdev %s\n",
|
|
reset_handle->priv[i]->netdev->name);
|
|
rtnl_lock();
|
|
dev_close(reset_handle->priv[i]->netdev);
|
|
rtnl_unlock();
|
|
break;
|
|
}
|
|
}
|
|
reset_handle = NULL;
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function process FW hang
|
|
*
|
|
* @param handle Pointer to structure moal_handle
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_process_hang(moal_handle *handle)
|
|
{
|
|
ENTER();
|
|
if (reset_handle == NULL) {
|
|
PRINTM(MMSG, "Start to process hanging\n");
|
|
reset_handle = handle;
|
|
mlan_ioctl(handle->pmlan_adapter, NULL);
|
|
queue_work(hang_workqueue, &hang_work);
|
|
#ifdef ANDROID_KERNEL
|
|
#define WAKE_LOCK_HANG 5000
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
|
|
__pm_wakeup_event(&reset_handle->ws, WAKE_LOCK_HANG);
|
|
#else
|
|
wake_lock_timeout(&reset_handle->wake_lock,
|
|
msecs_to_jiffies(WAKE_LOCK_HANG));
|
|
#endif
|
|
#endif
|
|
}
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Check if any interface is active
|
|
*
|
|
* @param handle A pointer to moal_handle
|
|
*
|
|
*
|
|
* @return MTRUE/MFALSE;
|
|
*/
|
|
t_u8
|
|
woal_is_any_interface_active(moal_handle *handle)
|
|
{
|
|
int i;
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
#ifdef STA_SUPPORT
|
|
if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) {
|
|
if (handle->priv[i]->media_connected == MTRUE)
|
|
return MTRUE;
|
|
}
|
|
#endif
|
|
#ifdef UAP_SUPPORT
|
|
if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) {
|
|
if (handle->priv[i]->bss_started == MTRUE)
|
|
return MTRUE;
|
|
}
|
|
#endif
|
|
}
|
|
return MFALSE;
|
|
}
|
|
|
|
/** @brief This function 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))
|
|
memcpy(&sec->param.passphrase.psk.pmk.pmk,
|
|
priv->pmk.pmk, MLAN_MAX_KEY_LENGTH);
|
|
if (memcmp(priv->pmk.pmk_r0, zero, MLAN_MAX_KEY_LENGTH)
|
|
&& memcmp(priv->pmk.pmk_r0_name, zero,
|
|
MLAN_MAX_PMKR0_NAME_LENGTH)) {
|
|
memcpy(&sec->param.passphrase.psk.pmk.pmk_r0,
|
|
priv->pmk.pmk_r0, MLAN_MAX_KEY_LENGTH);
|
|
memcpy(&sec->param.passphrase.psk.pmk.
|
|
pmk_r0_name, priv->pmk.pmk_r0_name,
|
|
MLAN_MAX_PMKR0_NAME_LENGTH);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
&& priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT
|
|
#endif
|
|
&& priv->bss_type != MLAN_BSS_TYPE_NAN) {
|
|
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 */
|
|
memcpy(priv->ip_addr, &ifa->ifa_address,
|
|
sizeof(ifa->ifa_address));
|
|
priv->ip_addr_type = IPADDR_TYPE_IPV4;
|
|
#ifdef STA_CFG80211
|
|
if (!hw_test && priv->roaming_enabled) {
|
|
sprintf(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)
|
|
&& !roamoffload_in_hs) {
|
|
sprintf(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;
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
|
|
#if defined(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)) {
|
|
memcpy(&ssid_bssid->ssid,
|
|
&bss_info.ssid,
|
|
sizeof(bss_info.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)) {
|
|
memcpy(&ssid_bssid->bssid, &bss_info.bssid,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
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 Get mode
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT)
|
|
*
|
|
* @return Wireless mode
|
|
*/
|
|
t_u32
|
|
woal_get_mode(moal_private *priv, t_u8 wait_option)
|
|
{
|
|
int ret = 0;
|
|
mlan_ds_bss *bss = NULL;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
t_u32 mode = 0;
|
|
|
|
ENTER();
|
|
|
|
#if defined(STA_WEXT) || defined(UAP_WEXT)
|
|
mode = priv->w_stats.status;
|
|
#endif
|
|
/* Allocate an IOCTL request buffer */
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
|
|
if (req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
bss = (mlan_ds_bss *)req->pbuf;
|
|
bss->sub_command = MLAN_OID_BSS_MODE;
|
|
req->req_id = MLAN_IOCTL_BSS;
|
|
req->action = MLAN_ACT_GET;
|
|
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, wait_option);
|
|
if (status == MLAN_STATUS_SUCCESS) {
|
|
switch (bss->param.bss_mode) {
|
|
case MLAN_BSS_MODE_INFRA:
|
|
mode = MW_MODE_INFRA;
|
|
break;
|
|
case MLAN_BSS_MODE_IBSS:
|
|
mode = MW_MODE_ADHOC;
|
|
break;
|
|
default:
|
|
mode = MW_MODE_AUTO;
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return mode;
|
|
}
|
|
|
|
/********************************************************
|
|
Local Functions
|
|
********************************************************/
|
|
/**
|
|
* @brief This function update the default firmware name
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void
|
|
woal_update_firmware_name(moal_handle *handle)
|
|
{
|
|
if (fw_name) {
|
|
handle->drv_mode.fw_name = fw_name;
|
|
} else {
|
|
if (!fw_serial || handle->fw_reload || fw_reload)
|
|
handle->drv_mode.fw_name = DEFAULT_WLAN_FW_NAME;
|
|
else
|
|
#if defined(UAP_SUPPORT) && defined(STA_SUPPORT)
|
|
handle->drv_mode.fw_name = DEFAULT_AP_STA_FW_NAME;
|
|
#else
|
|
#ifdef UAP_SUPPORT
|
|
handle->drv_mode.fw_name = DEFAULT_AP_FW_NAME;
|
|
#else
|
|
handle->drv_mode.fw_name = DEFAULT_FW_NAME;
|
|
#endif /* UAP_SUPPORT */
|
|
#endif /* UAP_SUPPORT && STA_SUPPORT */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
int last_wfd_index = 0;
|
|
#endif
|
|
#endif
|
|
|
|
ENTER();
|
|
|
|
/* Calculate number of interfaces */
|
|
#ifdef STA_SUPPORT
|
|
if (drv_mode_local & DRV_MODE_STA) {
|
|
if ((max_sta_bss < 1) || (max_sta_bss > MAX_STA_BSS)) {
|
|
PRINTM(MWARN,
|
|
"Unsupported max_sta_bss (%d), setting to default\n",
|
|
max_sta_bss);
|
|
max_sta_bss = DEF_STA_BSS;
|
|
}
|
|
intf_num += max_sta_bss;
|
|
}
|
|
#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;
|
|
}
|
|
#endif /* UAP_SUPPORT */
|
|
|
|
#if defined(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;
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
intf_num += max_vir_bss;
|
|
#endif
|
|
}
|
|
#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */
|
|
|
|
if (drv_mode_local & DRV_MODE_NAN) {
|
|
if ((max_nan_bss < 1) || (max_nan_bss > MAX_NAN_BSS)) {
|
|
PRINTM(MWARN,
|
|
"Unsupported max_nan_bss (%d), setting to default\n",
|
|
max_nan_bss);
|
|
max_nan_bss = DEF_NAN_BSS;
|
|
}
|
|
intf_num += max_nan_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 >= intf_num)
|
|
break;
|
|
bss_tbl[i].bss_type = MLAN_BSS_TYPE_STA;
|
|
bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II;
|
|
bss_tbl[i].active = MTRUE;
|
|
bss_tbl[i].bss_priority = 0;
|
|
bss_tbl[i].bss_num = j;
|
|
bss_tbl[i].bss_virtual = MFALSE;
|
|
i++;
|
|
}
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
#ifdef UAP_SUPPORT
|
|
if (drv_mode_local & DRV_MODE_UAP) {
|
|
for (j = 0; j < max_uap_bss; j++) {
|
|
if (i >= intf_num)
|
|
break;
|
|
bss_tbl[i].bss_type = MLAN_BSS_TYPE_UAP;
|
|
bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II;
|
|
bss_tbl[i].active = MTRUE;
|
|
bss_tbl[i].bss_priority = 0;
|
|
bss_tbl[i].bss_num = j;
|
|
bss_tbl[i].bss_virtual = MFALSE;
|
|
i++;
|
|
}
|
|
}
|
|
#endif /* UAP_SUPPORT */
|
|
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
if (drv_mode_local & DRV_MODE_WIFIDIRECT) {
|
|
for (j = 0; j < max_wfd_bss; j++) {
|
|
if (i >= intf_num)
|
|
break;
|
|
bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT;
|
|
bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II;
|
|
bss_tbl[i].active = MTRUE;
|
|
bss_tbl[i].bss_priority = 0;
|
|
bss_tbl[i].bss_num = j;
|
|
bss_tbl[i].bss_virtual = MFALSE;
|
|
i++;
|
|
}
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
last_wfd_index = j;
|
|
#endif
|
|
}
|
|
#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */
|
|
|
|
if (drv_mode_local & DRV_MODE_NAN) {
|
|
for (j = 0; j < max_nan_bss; j++) {
|
|
if (i >= intf_num)
|
|
break;
|
|
bss_tbl[i].bss_type = MLAN_BSS_TYPE_NAN;
|
|
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(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
/** append virtual interface at the end of table */
|
|
for (j = 0; j < max_vir_bss; j++) {
|
|
if (i >= intf_num)
|
|
break;
|
|
bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT;
|
|
bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II;
|
|
bss_tbl[i].active = MTRUE;
|
|
bss_tbl[i].bss_priority = 0;
|
|
bss_tbl[i].bss_num = j + last_wfd_index;
|
|
bss_tbl[i].bss_virtual = MTRUE;
|
|
i++;
|
|
}
|
|
#endif
|
|
#endif
|
|
/* Clear existing table, if any */
|
|
kfree(handle->drv_mode.bss_attr);
|
|
handle->drv_mode.bss_attr = NULL;
|
|
|
|
/* Create moal_drv_mode entry */
|
|
handle->drv_mode.drv_mode = 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;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
/**
|
|
* @brief This function read the initial parameter from device tress
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void
|
|
woal_init_from_dev_tree(void)
|
|
{
|
|
struct device_node *dt_node = NULL;
|
|
struct property *prop;
|
|
t_u32 data;
|
|
const char *string_data;
|
|
|
|
ENTER();
|
|
|
|
if (!dts_enable) {
|
|
PRINTM(MIOCTL, "DTS is disabled!");
|
|
return;
|
|
}
|
|
|
|
dt_node = of_find_node_by_name(NULL, "sd8xxx-wlan");
|
|
if (!dt_node) {
|
|
LEAVE();
|
|
return;
|
|
}
|
|
for_each_property_of_node(dt_node, prop) {
|
|
if (!strncmp(prop->name, "drv_mode", strlen("drv_mode"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "drv_mode=0x%x\n", data);
|
|
drv_mode = data;
|
|
}
|
|
}
|
|
#ifdef DEBUG_LEVEL1
|
|
else if (!strncmp(prop->name, "drvdbg", strlen("drvdbg"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "drvdbg=0x%x\n", data);
|
|
drvdbg = data;
|
|
}
|
|
}
|
|
#endif
|
|
else if (!strncmp
|
|
(prop->name, "dev_cap_mask", strlen("dev_cap_mask"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "dev_cap_mask=0x%x\n", data);
|
|
dev_cap_mask = data;
|
|
}
|
|
} else if (!strncmp(prop->name, "hw_test", strlen("hw_test"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "hw_test=0x%x\n", data);
|
|
hw_test = data;
|
|
}
|
|
}
|
|
#ifdef MFG_CMD_SUPPORT
|
|
else if (!strncmp(prop->name, "mfg_mode", strlen("mfg_mode"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "mfg_mode=0x%x\n", data);
|
|
mfg_mode = data;
|
|
}
|
|
}
|
|
#endif
|
|
else if (!strncmp(prop->name, "mac_addr", strlen("mac_addr"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
mac_addr = (char *)string_data;
|
|
PRINTM(MIOCTL, "mac_addr=%s\n", mac_addr);
|
|
}
|
|
} else if (!strncmp(prop->name, "fw_name", strlen("fw_name"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
fw_name = (char *)string_data;
|
|
PRINTM(MIOCTL, "fw_name=%s\n", fw_name);
|
|
}
|
|
}
|
|
#if defined(STA_WEXT) || defined(UAP_WEXT)
|
|
else if (!strncmp
|
|
(prop->name, "cfg80211_wext",
|
|
strlen("cfg80211_wext"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "cfg80211_wext=0x%x\n", data);
|
|
cfg80211_wext = data;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef STA_SUPPORT
|
|
else if (!strncmp(prop->name, "sta_name", strlen("sta_name"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
sta_name = (char *)string_data;
|
|
PRINTM(MIOCTL, "sta_name=%s\n", sta_name);
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
else if (!strncmp(prop->name, "wfd_name", strlen("wfd_name"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
wfd_name = (char *)string_data;
|
|
PRINTM(MIOCTL, "wfd_name=%s\n", wfd_name);
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
else if (!strncmp
|
|
(prop->name, "reg_alpha2", strlen("reg_alpha2"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
reg_alpha2 = (char *)string_data;
|
|
PRINTM(MIOCTL, "reg_alpha2=%s\n", reg_alpha2);
|
|
}
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
else if (!strncmp
|
|
(prop->name, "country_ie_ignore",
|
|
strlen("country_ie_ignore"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "country_ie_ignore=0x%x\n",
|
|
data);
|
|
country_ie_ignore = data;
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "beacon_hints",
|
|
strlen("beacon_hints"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "beacon_hints=0x%x\n", data);
|
|
beacon_hints = data;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
else if (!strncmp
|
|
(prop->name, "max_vir_bss", strlen("max_vir_bss"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "max_vir_bss=0x%x\n", data);
|
|
max_vir_bss = data;
|
|
}
|
|
} else if (!strncmp(prop->name, "p2p_enh", strlen("p2p_enh"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "p2p_enh=0x%x\n", data);
|
|
p2p_enh = data;
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "cfg80211_drcs",
|
|
strlen("cfg80211_drcs"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "cfg80211_drcs=0x%x\n", data);
|
|
cfg80211_drcs = data;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
else if (!strncmp
|
|
(prop->name, "dpd_data_cfg", strlen("dpd_data_cfg"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
dpd_data_cfg = (char *)string_data;
|
|
PRINTM(MIOCTL, "dpd_data_cfg=%s\n",
|
|
dpd_data_cfg);
|
|
}
|
|
} else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
init_cfg = (char *)string_data;
|
|
PRINTM(MIOCTL, "init_cfg=%s\n", init_cfg);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "cal_data_cfg",
|
|
strlen("cal_data_cfg"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
cal_data_cfg = (char *)string_data;
|
|
PRINTM(MIOCTL, "cal_data_cfg=%s\n",
|
|
cal_data_cfg);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "txpwrlimit_cfg",
|
|
strlen("txpwrlimit_cfg"))) {
|
|
if (!of_property_read_string
|
|
(dt_node, prop->name, &string_data)) {
|
|
txpwrlimit_cfg = (char *)string_data;
|
|
PRINTM(MIOCTL, "txpwrlimit_cfg=%s\n",
|
|
txpwrlimit_cfg);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "cntry_txpwr", strlen("cntry_txpwr"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
cntry_txpwr = data;
|
|
PRINTM(MIOCTL, "cntry_txpwr=%d\n", cntry_txpwr);
|
|
}
|
|
} else if (!strncmp(prop->name, "pmic", strlen("pmic"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
pmic = data;
|
|
PRINTM(MIOCTL, "pmic=%d\n", pmic);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "hs_wake_interval",
|
|
strlen("hs_wake_interval"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
hs_wake_interval = data;
|
|
PRINTM(MIOCTL, "hs_wake_interval=%d\n",
|
|
hs_wake_interval);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "indication_gpio",
|
|
strlen("indication_gpio"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
indication_gpio = (t_u8)data;
|
|
PRINTM(MIOCTL, "indication_gpio=%d\n",
|
|
indication_gpio);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "hs_mimo_switch",
|
|
strlen("hs_mimo_switch"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
hs_mimo_switch = data;
|
|
PRINTM(MIOCTL, "hs_mimo_switch=%d\n",
|
|
hs_mimo_switch);
|
|
}
|
|
}
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
else if (!strncmp
|
|
(prop->name, "GoAgeoutTime", strlen("GoAgeoutTime"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
GoAgeoutTime = data;
|
|
PRINTM(MIOCTL, "GoAgeoutTime=%d\n",
|
|
GoAgeoutTime);
|
|
}
|
|
}
|
|
#endif
|
|
else if (!strncmp(prop->name, "indrstcfg", strlen("indrstcfg"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
indrstcfg = data;
|
|
PRINTM(MIOCTL, "indrstcfg=%d\n", indrstcfg);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "drcs_chantime_mode",
|
|
strlen("drcs_chantime_mode"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
drcs_chantime_mode = data;
|
|
PRINTM(MIOCTL, "drcs_chantime_mode=%d\n",
|
|
drcs_chantime_mode);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "fixed_beacon_buffer",
|
|
strlen("fixed_beacon_buffer"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
fixed_beacon_buffer = data;
|
|
PRINTM(MIOCTL, "fixed_beacon_buffer=%d\n",
|
|
fixed_beacon_buffer);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "multi_dtim", strlen("multi_dtim"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
multi_dtim = data;
|
|
PRINTM(MIOCTL, "multi_dtim=%d\n", multi_dtim);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "inact_tmo", strlen("inact_tmo"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
inact_tmo = data;
|
|
PRINTM(MIOCTL, "inact_tmo=%d\n", inact_tmo);
|
|
}
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
else if (!strncmp
|
|
(prop->name, "dfs_offload", strlen("dfs_offload"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
dfs_offload = data;
|
|
PRINTM(MIOCTL, "dfs_offload=%d\n", dfs_offload);
|
|
}
|
|
}
|
|
#endif
|
|
else if (!strncmp
|
|
(prop->name, "roamoffload_in_hs",
|
|
strlen("roamoffload_in_hs"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
roamoffload_in_hs = data;
|
|
PRINTM(MIOCTL, "roamoffload_in_hs=%d\n",
|
|
roamoffload_in_hs);
|
|
}
|
|
} else if (!strncmp
|
|
(prop->name, "gtk_rekey_offload",
|
|
strlen("gtk_rekey_offload"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
gtk_rekey_offload = data;
|
|
PRINTM(MIOCTL, "gtk_rekey_offload=%d\n",
|
|
gtk_rekey_offload);
|
|
}
|
|
}
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
else if (!strncmp(prop->name, "host_mlme", strlen("host_mlme"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MIOCTL, "host_mlme=0x%x\n", data);
|
|
host_mlme = data;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef UAP_SUPPORT
|
|
else if (!strncmp
|
|
(prop->name, "uap_max_sta", strlen("uap_max_sta"))) {
|
|
if (!of_property_read_u32(dt_node, prop->name, &data)) {
|
|
PRINTM(MERROR, "uap_max_sta=0x%x\n", data);
|
|
uap_max_sta = data;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
LEAVE();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function initializes software
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status
|
|
woal_init_sw(moal_handle *handle)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
unsigned int i;
|
|
mlan_device device;
|
|
t_void *pmlan;
|
|
|
|
ENTER();
|
|
|
|
/* Initialize moal_handle structure */
|
|
handle->hardware_status = HardwareStatusInitializing;
|
|
handle->main_state = MOAL_STATE_IDLE;
|
|
|
|
#ifdef STA_SUPPORT
|
|
if ((drv_mode & DRV_MODE_STA)
|
|
#ifdef STA_WEXT
|
|
&& !IS_STA_WEXT(cfg80211_wext)
|
|
#endif
|
|
#ifdef STA_CFG80211
|
|
&& !IS_STA_CFG80211(cfg80211_wext)
|
|
#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
|
|
|
|
memcpy(handle->driver_version, driver_version, strlen(driver_version));
|
|
|
|
if (woal_update_drv_tbl(handle, 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;
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
handle->scan_chan_gap = DEF_SCAN_CHAN_GAP;
|
|
#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
|
|
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
|
|
|
|
#ifdef DFS_TESTING_SUPPORT
|
|
handle->cac_period_jiffies = 0;
|
|
#endif
|
|
#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
|
|
handle->mon_if = NULL;
|
|
|
|
#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 */
|
|
|
|
#if defined(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;
|
|
|
|
#ifdef MFG_CMD_SUPPORT
|
|
if (mfg_mode)
|
|
device.mfg_mode = (t_u32)MLAN_INIT_PARA_ENABLED;
|
|
|
|
#endif
|
|
#ifdef DEBUG_LEVEL1
|
|
device.drvdbg = drvdbg;
|
|
#endif
|
|
device.fixed_beacon_buffer = (t_u32)fixed_beacon_buffer;
|
|
device.auto_ds = (t_u32)auto_ds;
|
|
device.ps_mode = (t_u32)ps_mode;
|
|
device.max_tx_buf = (t_u32)max_tx_buf;
|
|
#if defined(STA_SUPPORT)
|
|
device.cfg_11d = (t_u32)cfg_11d;
|
|
#endif
|
|
device.indrstcfg = (t_u32)indrstcfg;
|
|
device.drcs_chantime_mode = (t_u32)drcs_chantime_mode;
|
|
|
|
if (rx_work == MLAN_INIT_PARA_ENABLED)
|
|
device.rx_work = MTRUE;
|
|
else if (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 (napi)
|
|
device.rx_work = MTRUE;
|
|
|
|
device.dev_cap_mask = dev_cap_mask;
|
|
|
|
device.multi_dtim = multi_dtim;
|
|
|
|
device.inact_tmo = inact_tmo;
|
|
device.hs_wake_interval = hs_wake_interval;
|
|
device.indication_gpio = indication_gpio;
|
|
device.hs_mimo_switch = hs_mimo_switch;
|
|
|
|
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;
|
|
}
|
|
memcpy(&device.callbacks, &woal_callbacks, sizeof(mlan_callbacks));
|
|
if (fw_region)
|
|
device.fw_region = MTRUE;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
if (MLAN_STATUS_SUCCESS == mlan_register(&device, &pmlan))
|
|
handle->pmlan_adapter = pmlan;
|
|
else
|
|
ret = MLAN_STATUS_FAILURE;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function frees the structure of moal_handle
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void
|
|
woal_free_moal_handle(moal_handle *handle)
|
|
{
|
|
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);
|
|
wiphy_free(handle->wiphy);
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
kfree(handle->regd);
|
|
#endif
|
|
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);
|
|
|
|
/* 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));
|
|
}
|
|
if (atomic_read(&handle->malloc_cons_count)) {
|
|
PRINTM(MERROR, "mlan has memory leak: malloc_cons_count=%d\n",
|
|
atomic_read(&handle->malloc_cons_count));
|
|
}
|
|
/* Free allocated memory for fwdump filename */
|
|
kfree(handle->fwdump_fname);
|
|
if (fwdump_fname) {
|
|
kfree(fwdump_fname);
|
|
fwdump_fname = NULL;
|
|
}
|
|
/* Free the moal handle itself */
|
|
kfree(handle);
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief WOAL get one line data from ASCII format data
|
|
*
|
|
* @param data Source data
|
|
* @param size Source data length
|
|
* @param line_pos Destination data
|
|
* @return routnine status
|
|
*/
|
|
static t_size
|
|
parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos)
|
|
{
|
|
t_u8 *src, *dest;
|
|
static t_s32 pos;
|
|
|
|
ENTER();
|
|
|
|
if (pos >= size) { /* reach the end */
|
|
pos = 0; /* Reset position for rfkill */
|
|
LEAVE();
|
|
return -1;
|
|
}
|
|
memset(line_pos, 0, MAX_LINE_LEN);
|
|
src = data + pos;
|
|
dest = line_pos;
|
|
|
|
while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size &&
|
|
*src != '\x0A' && *src != '\0') {
|
|
if (*src != ' ' && *src != '\t') /* parse space */
|
|
*dest++ = *src++;
|
|
else
|
|
src++;
|
|
pos++;
|
|
}
|
|
/* parse new line */
|
|
pos++;
|
|
*dest = '\0';
|
|
LEAVE();
|
|
return strlen(line_pos);
|
|
}
|
|
|
|
/**
|
|
* @brief Process register access request
|
|
* @param type_string String format Register type
|
|
* @param offset_string String format Register offset
|
|
* @param value_string String format Pointer to value
|
|
* @return MLAN_STATUS_SUCCESS--success, otherwise--fail
|
|
*/
|
|
static t_u32
|
|
woal_process_regrdwr(moal_handle *handle, t_u8 *type_string,
|
|
t_u8 *offset_string, t_u8 *value_string)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_FAILURE;
|
|
int type, offset, value;
|
|
pmlan_ioctl_req ioctl_req = NULL;
|
|
mlan_ds_reg_mem *reg = NULL;
|
|
|
|
ENTER();
|
|
|
|
/* Alloc ioctl_req */
|
|
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem));
|
|
|
|
if (ioctl_req == NULL) {
|
|
PRINTM(MERROR, "Can't alloc memory\n");
|
|
goto done;
|
|
}
|
|
|
|
if (MLAN_STATUS_SUCCESS != woal_atoi(&type, type_string))
|
|
goto done;
|
|
if (MLAN_STATUS_SUCCESS != woal_atoi(&offset, offset_string))
|
|
goto done;
|
|
if (MLAN_STATUS_SUCCESS != woal_atoi(&value, value_string))
|
|
goto done;
|
|
|
|
ioctl_req->req_id = MLAN_IOCTL_REG_MEM;
|
|
ioctl_req->action = MLAN_ACT_SET;
|
|
|
|
reg = (mlan_ds_reg_mem *)ioctl_req->pbuf;
|
|
reg->sub_command = MLAN_OID_REG_RW;
|
|
if (type < 5) {
|
|
reg->param.reg_rw.type = type;
|
|
} else {
|
|
PRINTM(MERROR, "Unsupported Type\n");
|
|
goto done;
|
|
}
|
|
reg->param.reg_rw.offset = offset;
|
|
reg->param.reg_rw.value = value;
|
|
|
|
/* request ioctl for STA */
|
|
ret = woal_request_ioctl(handle->priv[0], ioctl_req, MOAL_IOCTL_WAIT);
|
|
if (ret != MLAN_STATUS_SUCCESS)
|
|
goto done;
|
|
PRINTM(MINFO, "Register type: %d, offset: 0x%x, value: 0x%x\n", type,
|
|
offset, value);
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
|
|
done:
|
|
if (ret != MLAN_STATUS_PENDING)
|
|
kfree(ioctl_req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#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;
|
|
|
|
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;
|
|
}
|
|
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
|
|
if (req == NULL) {
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
bss = (mlan_ds_bss *)req->pbuf;
|
|
bss->sub_command = MLAN_OID_UAP_OPER_CTRL;
|
|
req->req_id = MLAN_IOCTL_BSS;
|
|
req->action = MLAN_ACT_SET;
|
|
|
|
bss->param.ap_oper_ctrl.ctrl_value =
|
|
(t_u16)((uap_oper_ctrl & 0xffff0000) >> 16);
|
|
bss->param.ap_oper_ctrl.chan_opt = (t_u16)(uap_oper_ctrl & 0xffff);
|
|
PRINTM(MMSG, "Uap oper_ctrl=0x%x chan_opt=0x%x\n",
|
|
bss->param.ap_oper_ctrl.ctrl_value,
|
|
bss->param.ap_oper_ctrl.chan_opt);
|
|
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
|
|
done:
|
|
if (ret != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
|
|
LEAVE();
|
|
return ret;
|
|
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief WOAL parse ASCII format data to MAC address
|
|
*
|
|
* @param handle MOAL handle
|
|
* @param data Source data
|
|
* @param size data length
|
|
* @return MLAN_STATUS_SUCCESS--success, otherwise--fail
|
|
*/
|
|
static t_u32
|
|
woal_process_init_cfg(moal_handle *handle, t_u8 *data, t_size size)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_FAILURE;
|
|
t_u8 *pos;
|
|
t_u8 *intf_s, *intf_e;
|
|
t_u8 s[MAX_LINE_LEN]; /* 1 line data */
|
|
t_size line_len;
|
|
t_u8 index = 0;
|
|
t_u32 i;
|
|
t_u8 bss_mac_addr[MAX_MAC_ADDR_LEN];
|
|
t_u8 bss_mac_name[MAX_PARAM_LEN];
|
|
t_u8 type[MAX_PARAM_LEN];
|
|
t_u8 offset[MAX_PARAM_LEN];
|
|
t_u8 value[MAX_PARAM_LEN];
|
|
|
|
ENTER();
|
|
|
|
while ((line_len = parse_cfg_get_line(data, size, s)) != -1) {
|
|
|
|
pos = s;
|
|
while (*pos == ' ' || *pos == '\t')
|
|
pos++;
|
|
|
|
if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') ||
|
|
*pos == '\n' || *pos == '\0')
|
|
continue; /* Needn't process this line */
|
|
|
|
/* Process MAC addr */
|
|
if (strncmp(pos, "mac_addr", 8) == 0) {
|
|
intf_s = strchr(pos, '=');
|
|
if (intf_s != NULL)
|
|
intf_e = strchr(intf_s, ':');
|
|
else
|
|
intf_e = NULL;
|
|
if (intf_s != NULL && intf_e != NULL) {
|
|
strncpy(bss_mac_addr, intf_e + 1,
|
|
MAX_MAC_ADDR_LEN - 1);
|
|
bss_mac_addr[MAX_MAC_ADDR_LEN - 1] = '\0';
|
|
if ((intf_e - intf_s) > MAX_PARAM_LEN) {
|
|
PRINTM(MERROR,
|
|
"Too long interface name %d\n",
|
|
__LINE__);
|
|
goto done;
|
|
}
|
|
strncpy(bss_mac_name, intf_s + 1,
|
|
intf_e - intf_s - 1);
|
|
bss_mac_name[intf_e - intf_s - 1] = '\0';
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
if (strcmp
|
|
(bss_mac_name,
|
|
handle->priv[i]->netdev->name) ==
|
|
0) {
|
|
memset(handle->priv[i]->
|
|
current_addr, 0,
|
|
ETH_ALEN);
|
|
PRINTM(MINFO,
|
|
"Interface name: %s mac: %s\n",
|
|
bss_mac_name,
|
|
bss_mac_addr);
|
|
woal_mac2u8(handle->priv[i]->
|
|
current_addr,
|
|
bss_mac_addr);
|
|
#if defined(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])) {
|
|
PRINTM(MERROR,
|
|
"Set MAC address failed\n");
|
|
goto done;
|
|
}
|
|
memcpy(handle->priv[i]->netdev->
|
|
dev_addr,
|
|
handle->priv[i]->
|
|
current_addr, ETH_ALEN);
|
|
index++; /* Mark found one interface matching */
|
|
}
|
|
}
|
|
} else {
|
|
PRINTM(MERROR, "Wrong config file format %d\n",
|
|
__LINE__);
|
|
goto done;
|
|
}
|
|
}
|
|
/* Process REG value */
|
|
else if (strncmp(pos, "wlan_reg", 8) == 0) {
|
|
intf_s = strchr(pos, '=');
|
|
if (intf_s != NULL)
|
|
intf_e = strchr(intf_s, ',');
|
|
else
|
|
intf_e = NULL;
|
|
if (intf_s != NULL && intf_e != NULL) {
|
|
/* Copy type */
|
|
strncpy(type, intf_s + 1, 1);
|
|
type[1] = '\0';
|
|
} else {
|
|
PRINTM(MERROR, "Wrong config file format %d\n",
|
|
__LINE__);
|
|
goto done;
|
|
}
|
|
intf_s = intf_e + 1;
|
|
intf_e = strchr(intf_s, ',');
|
|
if (intf_e != NULL) {
|
|
if ((intf_e - intf_s) >= MAX_PARAM_LEN) {
|
|
PRINTM(MERROR,
|
|
"Regsier offset is too long %d\n",
|
|
__LINE__);
|
|
goto done;
|
|
}
|
|
/* Copy offset */
|
|
strncpy(offset, intf_s, intf_e - intf_s);
|
|
offset[intf_e - intf_s] = '\0';
|
|
} else {
|
|
PRINTM(MERROR, "Wrong config file format %d\n",
|
|
__LINE__);
|
|
goto done;
|
|
}
|
|
intf_s = intf_e + 1;
|
|
if ((strlen(intf_s) >= MAX_PARAM_LEN)) {
|
|
PRINTM(MERROR, "Regsier value is too long %d\n",
|
|
__LINE__);
|
|
goto done;
|
|
}
|
|
/* Copy value */
|
|
strncpy(value, intf_s,
|
|
MIN((MAX_PARAM_LEN - 1), strlen(intf_s)));
|
|
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_process_regrdwr(handle, type, offset, value)) {
|
|
PRINTM(MERROR, "Access Reg failed\n");
|
|
goto done;
|
|
}
|
|
PRINTM(MINFO, "Reg type: %s, offset: %s, value: %s\n",
|
|
type, offset, value);
|
|
}
|
|
}
|
|
|
|
if (index == 0)
|
|
PRINTM(MINFO, "Can't find any matching MAC Address");
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief WOAL parse ASCII format raw data to hex format
|
|
*
|
|
* @param handle MOAL handle
|
|
* @param data Source data
|
|
* @param size data length
|
|
* @param wait_option wait option
|
|
* @return MLAN_STATUS_SUCCESS--success, otherwise--fail
|
|
*/
|
|
static t_u32
|
|
woal_process_hostcmd_cfg(moal_handle *handle, t_u8 *data, t_size size,
|
|
t_u8 wait_option)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
t_u8 *pos = data;
|
|
t_u8 *intf_s, *intf_e;
|
|
t_u8 *buf = NULL;
|
|
t_u8 *ptr = NULL;
|
|
t_u32 cmd_len = 0;
|
|
t_u8 start_raw = MFALSE;
|
|
gfp_t flag;
|
|
|
|
#define CMD_STR "MRVL_CMDhostcmd"
|
|
#define CMD_BUF_LEN 2048
|
|
|
|
ENTER();
|
|
flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
|
|
buf = kzalloc(CMD_BUF_LEN, flag);
|
|
if (!buf) {
|
|
PRINTM(MERROR, "Could not allocate buffer space!\n");
|
|
goto done;
|
|
}
|
|
ptr = buf;
|
|
strcpy(ptr, CMD_STR);
|
|
ptr = buf + strlen(CMD_STR) + sizeof(t_u32);
|
|
while ((pos - data) < size) {
|
|
while (*pos == ' ' || *pos == '\t')
|
|
pos++;
|
|
if (*pos == '#') { /* Line comment */
|
|
while (*pos != '\n')
|
|
pos++;
|
|
pos++;
|
|
}
|
|
if ((*pos == '\r' && *(pos + 1) == '\n') ||
|
|
*pos == '\n' || *pos == '\0') {
|
|
pos++;
|
|
continue; /* Needn't process this line */
|
|
}
|
|
|
|
if (*pos == '}') {
|
|
cmd_len =
|
|
*((t_u16 *)(buf + strlen(CMD_STR) +
|
|
sizeof(t_u32) + sizeof(t_u16)));
|
|
memcpy(buf + strlen(CMD_STR), &cmd_len, sizeof(t_u32));
|
|
|
|
/* 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
|
|
|
|
/**
|
|
* @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_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
|
|
* @return MLAN_STATUS_SUCCESS--success, otherwise--fail
|
|
*/
|
|
static t_u32
|
|
woal_set_user_init_data(moal_handle *handle, int type, t_u8 wait_option)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_FAILURE;
|
|
t_u8 *cfg_data = NULL;
|
|
t_size len;
|
|
|
|
ENTER();
|
|
|
|
if (type == INIT_CFG_DATA) {
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg,
|
|
handle->hotplug_device, GFP_KERNEL, handle,
|
|
woal_request_init_cfg_data_callback)) < 0) {
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_cfg_data_callback)) < 0) {
|
|
#else
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, init_cfg, handle->hotplug_device,
|
|
handle,
|
|
woal_request_init_cfg_data_callback)) < 0) {
|
|
#endif
|
|
#endif
|
|
PRINTM(MERROR,
|
|
"Init config file request_firmware_nowait() failed\n");
|
|
goto done;
|
|
}
|
|
handle->init_user_conf_wait_flag = MFALSE;
|
|
wait_event_interruptible(handle->init_user_conf_wait_q,
|
|
handle->
|
|
init_user_conf_wait_flag);
|
|
} else {
|
|
if ((request_firmware
|
|
(&handle->init_cfg_data, init_cfg,
|
|
handle->hotplug_device)) < 0) {
|
|
PRINTM(MERROR,
|
|
"Init config file request_firmware() failed\n");
|
|
goto done;
|
|
}
|
|
}
|
|
} else if (type == COUNTRY_POWER_TABLE) {
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG,
|
|
country_txpwrlimit, handle->hotplug_device,
|
|
GFP_KERNEL, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG,
|
|
country_txpwrlimit, handle->hotplug_device,
|
|
handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#else
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, country_txpwrlimit,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#endif
|
|
#endif
|
|
PRINTM(MERROR,
|
|
"country txpwrlimit config file request_firmware_nowait() failed\n");
|
|
goto done;
|
|
}
|
|
handle->init_user_conf_wait_flag = MFALSE;
|
|
wait_event_interruptible(handle->init_user_conf_wait_q,
|
|
handle->
|
|
init_user_conf_wait_flag);
|
|
} else {
|
|
int status =
|
|
request_firmware(&handle->user_data,
|
|
country_txpwrlimit,
|
|
handle->hotplug_device);
|
|
/* File does not exist, skip download */
|
|
if (status == -ENOENT) {
|
|
PRINTM(MIOCTL,
|
|
"Country power table file does not exist\n");
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
} else if (status) {
|
|
PRINTM(MERROR,
|
|
"country txpwrlimit config file request_firmware() failed\n");
|
|
goto done;
|
|
}
|
|
}
|
|
} else if (type == INIT_HOSTCMD_CFG_DATA) {
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, init_hostcmd_cfg,
|
|
handle->hotplug_device, GFP_KERNEL, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, init_hostcmd_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#else
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, init_hostcmd_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#endif
|
|
#endif
|
|
PRINTM(MERROR,
|
|
"Init hostcmd config file request_firmware_nowait() failed\n");
|
|
goto done;
|
|
}
|
|
handle->init_user_conf_wait_flag = MFALSE;
|
|
wait_event_interruptible(handle->init_user_conf_wait_q,
|
|
handle->
|
|
init_user_conf_wait_flag);
|
|
} else {
|
|
if ((request_firmware
|
|
(&handle->user_data, init_hostcmd_cfg,
|
|
handle->hotplug_device)) < 0) {
|
|
PRINTM(MERROR,
|
|
"Init hostcmd config file request_firmware() failed\n");
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (handle->user_data) {
|
|
cfg_data = (t_u8 *)(handle->user_data)->data;
|
|
len = (handle->user_data)->size;
|
|
if (type == INIT_HOSTCMD_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);
|
|
|
|
/**
|
|
* @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();
|
|
|
|
#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 (mfg_mode == MLAN_INIT_PARA_ENABLED)
|
|
goto done;
|
|
#endif
|
|
|
|
if (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 (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;
|
|
}
|
|
}
|
|
|
|
#ifdef UAP_SUPPORT
|
|
if (uap_oper_ctrl)
|
|
woal_set_uap_operation_ctrl(handle);
|
|
#endif
|
|
|
|
#ifdef MFG_CMD_SUPPORT
|
|
done:
|
|
#endif
|
|
err:
|
|
if (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);
|
|
|
|
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 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;
|
|
int status;
|
|
|
|
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->fw_reload == FW_RELOAD_NO_EMULATION)
|
|
fw.fw_reload = FW_RELOAD_NO_EMULATION;
|
|
else
|
|
fw.fw_reload = 0;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
ret = mlan_dnld_fw(handle->pmlan_adapter, &fw);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
if (ret == MLAN_STATUS_FAILURE) {
|
|
PRINTM(MERROR,
|
|
"WLAN: Fail download FW with nowwait: %d\n",
|
|
req_fw_nowait);
|
|
goto done;
|
|
}
|
|
PRINTM(MMSG, "WLAN FW is active\n");
|
|
}
|
|
|
|
/** data request */
|
|
memset(¶m, 0, sizeof(mlan_init_param));
|
|
|
|
if (dpd_data_cfg && strncmp(dpd_data_cfg, "none", strlen("none"))) {
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, dpd_data_cfg,
|
|
handle->hotplug_device, GFP_KERNEL, handle,
|
|
woal_request_init_dpd_conf_callback)) < 0) {
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, dpd_data_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_dpd_conf_callback)) < 0) {
|
|
#else
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, dpd_data_cfg, handle->hotplug_device,
|
|
handle,
|
|
woal_request_init_dpd_conf_callback)) < 0) {
|
|
#endif
|
|
#endif
|
|
PRINTM(MERROR,
|
|
"DPD data request_firmware_nowait() failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
handle->init_user_conf_wait_flag = MFALSE;
|
|
wait_event_interruptible(handle->init_user_conf_wait_q,
|
|
handle->
|
|
init_user_conf_wait_flag);
|
|
} else {
|
|
status = request_firmware(&handle->dpd_data,
|
|
dpd_data_cfg,
|
|
handle->hotplug_device);
|
|
if (status < 0 && status != -ENOENT) {
|
|
PRINTM(MERROR,
|
|
"DPD data request_firmware() failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
}
|
|
if (handle->dpd_data) {
|
|
param.pdpd_data_buf = (t_u8 *)handle->dpd_data->data;
|
|
param.dpd_data_len = handle->dpd_data->size;
|
|
} else {
|
|
param.dpd_data_len = UNKNOW_DPD_LENGTH;
|
|
}
|
|
}
|
|
if (txpwrlimit_cfg && strncmp(txpwrlimit_cfg, "none", strlen("none"))) {
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, txpwrlimit_cfg,
|
|
handle->hotplug_device, GFP_KERNEL, handle,
|
|
woal_request_init_txpwr_conf_callback)) < 0) {
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, txpwrlimit_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_txpwr_conf_callback)) < 0) {
|
|
#else
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, txpwrlimit_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_txpwr_conf_callback)) < 0) {
|
|
#endif
|
|
#endif
|
|
PRINTM(MERROR, "Region txpwrlimit cfg data "
|
|
"request_firmware_nowait() failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
handle->init_user_conf_wait_flag = MFALSE;
|
|
wait_event_interruptible(handle->init_user_conf_wait_q,
|
|
handle->
|
|
init_user_conf_wait_flag);
|
|
} else {
|
|
if ((request_firmware
|
|
(&handle->txpwr_data, txpwrlimit_cfg,
|
|
handle->hotplug_device)) < 0) {
|
|
PRINTM(MERROR,
|
|
"Region txpwrlimit cfg data "
|
|
"request_firmware() failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
}
|
|
if (handle->txpwr_data) {
|
|
param.ptxpwr_data_buf =
|
|
(t_u8 *)handle->txpwr_data->data;
|
|
param.txpwr_data_len = handle->txpwr_data->size;
|
|
}
|
|
}
|
|
|
|
if (cal_data_cfg && strncmp(cal_data_cfg, "none", strlen("none"))) {
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, cal_data_cfg,
|
|
handle->hotplug_device, GFP_KERNEL, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, FW_ACTION_HOTPLUG, cal_data_cfg,
|
|
handle->hotplug_device, handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#else
|
|
if ((request_firmware_nowait
|
|
(THIS_MODULE, cal_data_cfg, handle->hotplug_device,
|
|
handle,
|
|
woal_request_init_user_conf_callback)) < 0) {
|
|
#endif
|
|
#endif
|
|
PRINTM(MERROR,
|
|
"Cal data request_firmware_nowait() failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
handle->init_user_conf_wait_flag = MFALSE;
|
|
wait_event_interruptible(handle->init_user_conf_wait_q,
|
|
handle->
|
|
init_user_conf_wait_flag);
|
|
} else {
|
|
if ((request_firmware
|
|
(&handle->user_data, cal_data_cfg,
|
|
handle->hotplug_device)) < 0) {
|
|
PRINTM(MERROR,
|
|
"Cal data request_firmware() failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
}
|
|
} else if (!cal_data_cfg) {
|
|
PRINTM(MERROR,
|
|
"Please add cal_data_cfg for 8887/8977/8997/8987/9098/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;
|
|
}
|
|
|
|
handle->hardware_status = HardwareStatusFwReady;
|
|
if (ret != MLAN_STATUS_SUCCESS)
|
|
goto done;
|
|
if (handle->fw_reload)
|
|
goto done;
|
|
handle->init_wait_q_woken = MFALSE;
|
|
|
|
ret = mlan_set_init_param(handle->pmlan_adapter, ¶m);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
ret = mlan_init_fw(handle->pmlan_adapter);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
if (ret == MLAN_STATUS_FAILURE)
|
|
goto done;
|
|
else if (ret == MLAN_STATUS_SUCCESS) {
|
|
handle->hardware_status = HardwareStatusReady;
|
|
goto done;
|
|
}
|
|
/* Wait for mlan_init to complete */
|
|
/* Wait for mlan_init to complete */
|
|
wait_event_timeout(handle->init_wait_q,
|
|
handle->init_wait_q_woken, 5 * HZ);
|
|
|
|
if (handle->hardware_status != HardwareStatusReady) {
|
|
woal_moal_debug_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY),
|
|
handle, MTRUE);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
done:
|
|
if (handle->dpd_data) {
|
|
release_firmware(handle->dpd_data);
|
|
handle->dpd_data = NULL;
|
|
}
|
|
if (handle->txpwr_data) {
|
|
release_firmware(handle->txpwr_data);
|
|
handle->txpwr_data = NULL;
|
|
}
|
|
if (handle->user_data) {
|
|
release_firmware(handle->user_data);
|
|
handle->user_data = NULL;
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Request firmware DPC
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param firmware A pointer to firmware image
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status
|
|
woal_request_fw_dpc(moal_handle *handle, const struct firmware *firmware)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
struct timeval tstamp;
|
|
|
|
ENTER();
|
|
|
|
if (!firmware) {
|
|
woal_get_monotonic_time(&tstamp);
|
|
if (tstamp.tv_sec >
|
|
(handle->req_fw_time.tv_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)
|
|
{
|
|
ENTER();
|
|
woal_request_fw_dpc((moal_handle *)context, firmware);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
if (firmware)
|
|
release_firmware(firmware);
|
|
#endif
|
|
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)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
int err;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MMSG, "Request firmware: %s\n", handle->drv_mode.fw_name);
|
|
if (req_fw_nowait) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
|
err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
|
|
handle->drv_mode.fw_name,
|
|
handle->hotplug_device,
|
|
GFP_KERNEL, handle,
|
|
woal_request_fw_callback);
|
|
#else
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
|
|
err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
|
|
handle->drv_mode.fw_name,
|
|
handle->hotplug_device, handle,
|
|
woal_request_fw_callback);
|
|
#else
|
|
err = request_firmware_nowait(THIS_MODULE,
|
|
handle->drv_mode.fw_name,
|
|
handle->hotplug_device, handle,
|
|
woal_request_fw_callback);
|
|
#endif
|
|
#endif
|
|
if (err < 0) {
|
|
PRINTM(MFATAL,
|
|
"WLAN: request_firmware_nowait() failed, error code = %d\n",
|
|
err);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
}
|
|
} else {
|
|
err = request_firmware(&handle->firmware,
|
|
handle->drv_mode.fw_name,
|
|
handle->hotplug_device);
|
|
if (err < 0) {
|
|
PRINTM(MFATAL,
|
|
"WLAN: request_firmware() failed, error code = %d\n",
|
|
err);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
} else {
|
|
if (handle->fw_reload)
|
|
ret = woal_init_fw_dpc(handle);
|
|
else
|
|
ret = woal_request_fw_dpc(handle,
|
|
handle->firmware);
|
|
release_firmware(handle->firmware);
|
|
}
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function initializes firmware
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
static mlan_status
|
|
woal_init_fw(moal_handle *handle)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
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");
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
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
|
|
*/
|
|
static void
|
|
woal_fill_mlan_buffer(moal_private *priv,
|
|
mlan_buffer *pmbuf, struct sk_buff *skb)
|
|
{
|
|
struct timeval tstamp;
|
|
struct ethhdr *eth;
|
|
t_u8 tid;
|
|
dot11_txcontrol *txcontrol;
|
|
t_u8 tx_ctrl_flag = MFALSE;
|
|
int i = 0;
|
|
ENTER();
|
|
/*
|
|
* skb->priority values from 256->263 are magic values to
|
|
* directly indicate a specific 802.1d priority. This is used
|
|
* to allow 802.1d priority to be passed directly in from VLAN
|
|
* tags, etc.
|
|
*/
|
|
if (IS_SKB_MAGIC_VLAN(skb)) {
|
|
tid = GET_VLAN_PRIO(skb);
|
|
} else {
|
|
eth = (struct ethhdr *)skb->data;
|
|
|
|
switch (eth->h_proto) {
|
|
case __constant_htons(ETH_P_IP):
|
|
tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET];
|
|
if (tid == 0xFF)
|
|
tid = (IPTOS_PREC(SKB_TOS(skb)) >>
|
|
IPTOS_OFFSET);
|
|
PRINTM(MDAT_D,
|
|
"packet type ETH_P_IP: dscp[%x], map[%x], tid=%d\n",
|
|
SKB_TOS(skb) >> DSCP_OFFSET,
|
|
priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET],
|
|
tid);
|
|
break;
|
|
case __constant_htons(ETH_P_IPV6):
|
|
tid = SKB_TIDV6(skb);
|
|
PRINTM(MDAT_D,
|
|
"packet type ETH_P_IPV6: %04x, tid=%#x prio=%#x\n",
|
|
eth->h_proto, tid, skb->priority);
|
|
break;
|
|
case __constant_htons(ETH_P_ARP):
|
|
tid = 0;
|
|
PRINTM(MDATA, "ARP packet %04x\n", eth->h_proto);
|
|
break;
|
|
default:
|
|
tid = 0;
|
|
if (priv->tx_protocols.protocol_num) {
|
|
for (i = 0; i < priv->tx_protocols.protocol_num;
|
|
i++) {
|
|
if (eth->h_proto ==
|
|
__constant_htons(priv->tx_protocols.
|
|
protocols[i]))
|
|
tx_ctrl_flag = MTRUE;
|
|
}
|
|
}
|
|
if (tx_ctrl_flag) {
|
|
txcontrol =
|
|
(dot11_txcontrol *) (skb->data +
|
|
sizeof(struct
|
|
ethhdr));
|
|
pmbuf->u.tx_info.data_rate =
|
|
txcontrol->datarate;
|
|
pmbuf->u.tx_info.channel = txcontrol->channel;
|
|
pmbuf->u.tx_info.bw = txcontrol->bw;
|
|
pmbuf->u.tx_info.tx_power.val =
|
|
txcontrol->power;
|
|
pmbuf->u.tx_info.retry_limit =
|
|
txcontrol->retry_limit;
|
|
tid = txcontrol->priority;
|
|
memmove(skb->data + sizeof(dot11_txcontrol),
|
|
skb->data, sizeof(struct ethhdr));
|
|
skb_pull(skb, sizeof(dot11_txcontrol));
|
|
pmbuf->flags |= MLAN_BUF_FLAG_TX_CTRL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
skb->priority = tid;
|
|
|
|
/* Record the current time the packet was queued; used to determine
|
|
* the amount of time the packet was queued in the driver before it
|
|
* was sent to the firmware. The delay is then sent along with the
|
|
* packet to the firmware for aggregate delay calculation for stats
|
|
* and MSDU lifetime expiry.
|
|
*/
|
|
woal_get_monotonic_time(&tstamp);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
|
|
skb->tstamp = timeval_to_ktime(tstamp);
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
|
|
skb_set_timestamp(skb, &tstamp);
|
|
#else
|
|
memcpy(&skb->stamp, &tstamp, sizeof(skb->stamp));
|
|
#endif
|
|
|
|
pmbuf->pdesc = skb;
|
|
pmbuf->pbuf = skb->head + sizeof(mlan_buffer);
|
|
pmbuf->buf_pa = 0;
|
|
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 = (t_u32)tstamp.tv_sec;
|
|
pmbuf->in_ts_usec = (t_u32)tstamp.tv_usec;
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function opens the network device for monitor interface
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
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
|
|
*/
|
|
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
|
|
*/
|
|
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
|
|
*/
|
|
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
|
|
*/
|
|
int
|
|
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(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 & 0x0080)
|
|
qos_len = 2;
|
|
if ((dot11_hdr->frame_control & 0x0300) == 0x0300)
|
|
dot11_hdr_len += 6;
|
|
|
|
memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr));
|
|
memcpy(src_mac_addr, dot11_hdr->addr2, 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;
|
|
memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr));
|
|
memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr,
|
|
sizeof(src_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
|
|
*/
|
|
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
|
|
* @param addr MAC address to set
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
|
|
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
|
|
*
|
|
* @return A pointer to monitor_iface
|
|
*/
|
|
monitor_iface *
|
|
woal_prepare_mon_if(moal_private *priv,
|
|
const char *name, unsigned char name_assign_type)
|
|
{
|
|
int ret = 0;
|
|
struct net_device *ndev = NULL;
|
|
monitor_iface *mon_if = NULL;
|
|
|
|
ENTER();
|
|
|
|
#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);
|
|
memcpy(mon_if->ifname, ndev->name, 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->radiotap_enabled = 1;
|
|
mon_if->flag = 1;
|
|
|
|
fail:
|
|
if (ret) {
|
|
if (ndev)
|
|
free_netdev(ndev);
|
|
LEAVE();
|
|
return NULL;
|
|
}
|
|
|
|
LEAVE();
|
|
return mon_if;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
|
|
static struct device_type wlan_type = {.name = "wlan", };
|
|
#endif
|
|
|
|
#ifdef STA_SUPPORT
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
/** Network device handlers */
|
|
const struct net_device_ops woal_netdev_ops = {
|
|
.ndo_open = woal_open,
|
|
.ndo_start_xmit = woal_hard_start_xmit,
|
|
.ndo_stop = woal_close,
|
|
.ndo_do_ioctl = woal_do_ioctl,
|
|
.ndo_set_mac_address = woal_set_mac_address,
|
|
.ndo_tx_timeout = woal_tx_timeout,
|
|
.ndo_get_stats = woal_get_stats,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
.ndo_set_rx_mode = woal_set_multicast_list,
|
|
#else
|
|
.ndo_set_multicast_list = woal_set_multicast_list,
|
|
#endif
|
|
.ndo_select_queue = woal_select_queue,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function initializes the private structure
|
|
* and dev structure for station mode
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status
|
|
woal_init_sta_dev(struct net_device *dev, moal_private *priv)
|
|
{
|
|
ENTER();
|
|
|
|
/* Setup the OS Interface to our functions */
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29)
|
|
dev->open = woal_open;
|
|
dev->hard_start_xmit = woal_hard_start_xmit;
|
|
dev->stop = woal_close;
|
|
dev->do_ioctl = woal_do_ioctl;
|
|
dev->set_mac_address = woal_set_mac_address;
|
|
dev->tx_timeout = woal_tx_timeout;
|
|
dev->get_stats = woal_get_stats;
|
|
dev->set_multicast_list = woal_set_multicast_list;
|
|
#else
|
|
dev->netdev_ops = &woal_netdev_ops;
|
|
#endif
|
|
dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
|
|
dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer)
|
|
+ priv->extra_tx_head_len;
|
|
#else
|
|
dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer)
|
|
+ priv->extra_tx_head_len;
|
|
#endif
|
|
#ifdef STA_WEXT
|
|
if (IS_STA_WEXT(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(cfg80211_wext))
|
|
init_waitqueue_head(&priv->ft_wait_q);
|
|
#endif
|
|
#ifdef STA_CFG80211
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
if (IS_STA_CFG80211(cfg80211_wext)) {
|
|
if (host_mlme)
|
|
init_waitqueue_head(&priv->host_mlme_wait_q);
|
|
}
|
|
#endif
|
|
#endif
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
#ifdef UAP_SUPPORT
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
/** Network device handlers */
|
|
const struct net_device_ops woal_uap_netdev_ops = {
|
|
.ndo_open = woal_open,
|
|
.ndo_start_xmit = woal_hard_start_xmit,
|
|
.ndo_stop = woal_close,
|
|
.ndo_do_ioctl = woal_uap_do_ioctl,
|
|
.ndo_set_mac_address = woal_set_mac_address,
|
|
.ndo_tx_timeout = woal_tx_timeout,
|
|
.ndo_get_stats = woal_get_stats,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
.ndo_set_rx_mode = woal_uap_set_multicast_list,
|
|
#else
|
|
.ndo_set_multicast_list = woal_uap_set_multicast_list,
|
|
#endif
|
|
.ndo_select_queue = woal_select_queue,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function initializes the private structure
|
|
* and dev structure for uap mode
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS
|
|
*/
|
|
mlan_status
|
|
woal_init_uap_dev(struct net_device *dev, moal_private *priv)
|
|
{
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
/* Setup the OS Interface to our functions */
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29)
|
|
dev->open = woal_open;
|
|
dev->hard_start_xmit = woal_hard_start_xmit;
|
|
dev->stop = woal_close;
|
|
dev->set_mac_address = woal_set_mac_address;
|
|
dev->tx_timeout = woal_tx_timeout;
|
|
dev->get_stats = woal_get_stats;
|
|
dev->do_ioctl = woal_uap_do_ioctl;
|
|
dev->set_multicast_list = woal_uap_set_multicast_list;
|
|
#else
|
|
dev->netdev_ops = &woal_uap_netdev_ops;
|
|
#endif
|
|
dev->watchdog_timeo = MRVDRV_DEFAULT_UAP_WATCHDOG_TIMEOUT;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
|
|
dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer)
|
|
+ priv->extra_tx_head_len;
|
|
#else
|
|
dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer)
|
|
+ priv->extra_tx_head_len;
|
|
#endif
|
|
#ifdef UAP_WEXT
|
|
if (IS_UAP_WEXT(cfg80211_wext)) {
|
|
#if WIRELESS_EXT < 21
|
|
dev->get_wireless_stats = woal_get_uap_wireless_stats;
|
|
#endif
|
|
dev->wireless_handlers =
|
|
(struct iw_handler_def *)&woal_uap_handler_def;
|
|
}
|
|
#endif /* UAP_WEXT */
|
|
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
|
|
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
#endif /* UAP_SUPPORT */
|
|
|
|
/**
|
|
* @brief This function adds a new interface. It will
|
|
* allocate, initialize and register the device.
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param bss_index BSS index number
|
|
* @param bss_type BSS type
|
|
*
|
|
* @return A pointer to the new priv structure
|
|
*/
|
|
moal_private *
|
|
woal_add_interface(moal_handle *handle, t_u8 bss_index, t_u8 bss_type)
|
|
{
|
|
struct net_device *dev = NULL;
|
|
moal_private *priv = NULL;
|
|
char name[256];
|
|
int i = 0;
|
|
#ifdef UAP_CFG80211
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
char csa_str[256];
|
|
#endif
|
|
#endif
|
|
ENTER();
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
#define MAX_WMM_QUEUE 4
|
|
/* Allocate an Ethernet device */
|
|
dev = alloc_etherdev_mq(sizeof(moal_private), MAX_WMM_QUEUE);
|
|
#else
|
|
dev = alloc_etherdev(sizeof(moal_private));
|
|
#endif
|
|
if (!dev) {
|
|
PRINTM(MFATAL, "Init virtual ethernet device failed\n");
|
|
goto error;
|
|
}
|
|
/* Allocate device name */
|
|
#ifdef STA_SUPPORT
|
|
memset(name, 0, sizeof(name));
|
|
if (sta_name)
|
|
snprintf(name, sizeof(name), "%s%%d", sta_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 (uap_name)
|
|
snprintf(name, sizeof(name), "%s%%d", 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
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
memset(name, 0, sizeof(name));
|
|
if (wfd_name)
|
|
snprintf(name, sizeof(name), "%s%%d", 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 (nan_name)
|
|
snprintf(name, sizeof(name), "%s%%d", nan_name);
|
|
else
|
|
snprintf(name, sizeof(name), "%s", default_nan_name);
|
|
if ((bss_type == MLAN_BSS_TYPE_NAN) && (dev_alloc_name(dev, name) < 0)) {
|
|
PRINTM(MERROR, "Could not allocate nan 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;
|
|
#if defined(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_NAN)
|
|
priv->bss_role = MLAN_BSS_ROLE_STA;
|
|
|
|
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);
|
|
#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
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
|| bss_type == MLAN_BSS_TYPE_WIFIDIRECT
|
|
#endif
|
|
|| bss_type == MLAN_BSS_TYPE_NAN)
|
|
woal_init_sta_dev(dev, priv);
|
|
#endif
|
|
#ifdef UAP_SUPPORT
|
|
if (bss_type == MLAN_BSS_TYPE_UAP) {
|
|
if (MLAN_STATUS_SUCCESS != woal_init_uap_dev(dev, priv))
|
|
goto error;
|
|
}
|
|
#endif
|
|
if (!handle->priv_num
|
|
#ifdef MFG_CMD_SUPPORT
|
|
&& (mfg_mode != MLAN_INIT_PARA_ENABLED)
|
|
#endif
|
|
) {
|
|
if (init_cfg) {
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_user_init_data(handle, INIT_CFG_DATA,
|
|
MOAL_IOCTL_WAIT)) {
|
|
PRINTM(MFATAL,
|
|
"Set user init data and param failed\n");
|
|
goto error;
|
|
}
|
|
}
|
|
if (init_hostcmd_cfg) {
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_user_init_data(handle,
|
|
INIT_HOSTCMD_CFG_DATA,
|
|
MOAL_IOCTL_WAIT)) {
|
|
PRINTM(MFATAL,
|
|
"Set user init hostcmd data and param failed\n");
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
handle->priv_num++;
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
if (!priv->phandle->wiphy && IS_STA_OR_UAP_CFG80211(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(cfg80211_wext)) {
|
|
if (bss_type == MLAN_BSS_TYPE_STA
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
|| bss_type == MLAN_BSS_TYPE_WIFIDIRECT
|
|
#endif
|
|
#endif
|
|
|| bss_type == MLAN_BSS_TYPE_NAN)
|
|
/* Register cfg80211 for STA or Wifi direct */
|
|
if (woal_register_sta_cfg80211(dev, bss_type)) {
|
|
PRINTM(MERROR,
|
|
"Cannot register STA with cfg80211\n");
|
|
goto error;
|
|
}
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
#endif /* STA_CFG80211 */
|
|
#ifdef UAP_CFG80211
|
|
#ifdef UAP_SUPPORT
|
|
if ((priv->bss_role == MLAN_BSS_ROLE_UAP) &&
|
|
IS_UAP_CFG80211(cfg80211_wext)) {
|
|
/* Register cfg80211 for UAP */
|
|
if (woal_register_uap_cfg80211(dev, bss_type)) {
|
|
PRINTM(MERROR, "Cannot register UAP with cfg80211\n");
|
|
goto error;
|
|
}
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
strcpy(csa_str, "CSA");
|
|
strcat(csa_str, name);
|
|
priv->csa_workqueue =
|
|
alloc_workqueue(csa_str,
|
|
WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
|
|
if (!priv->csa_workqueue) {
|
|
PRINTM(MERROR, "cannot alloc csa workqueue \n");
|
|
goto error;
|
|
}
|
|
INIT_DELAYED_WORK(&priv->csa_work, woal_csa_work_queue);
|
|
#endif
|
|
#endif
|
|
#endif /*UAP_CFG80211 */
|
|
|
|
/* Initialize priv structure */
|
|
woal_init_priv(priv, MOAL_IOCTL_WAIT);
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
SET_NETDEV_DEV(dev, handle->hotplug_device);
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
|
|
SET_NETDEV_DEVTYPE(dev, &wlan_type);
|
|
#endif
|
|
|
|
/* Register network device */
|
|
if (register_netdev(dev)) {
|
|
PRINTM(MERROR, "Cannot register virtual network device\n");
|
|
goto error;
|
|
}
|
|
netif_carrier_off(dev);
|
|
woal_stop_queue(dev);
|
|
|
|
PRINTM(MINFO, "%s: Marvell 802.11 Adapter\n", dev->name);
|
|
|
|
if (bss_type == MLAN_BSS_TYPE_STA ||
|
|
priv->bss_type == MLAN_BSS_TYPE_UAP) {
|
|
mlan_fw_info fw_info;
|
|
woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
|
|
|
|
for (i = 0; i < handle->histogram_table_num; i++) {
|
|
priv->hist_data[i] = kmalloc(sizeof(hgm_data) +
|
|
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;
|
|
}
|
|
}
|
|
if (priv->hist_data)
|
|
woal_hist_data_reset(priv);
|
|
}
|
|
#ifdef CONFIG_PROC_FS
|
|
woal_create_proc_entry(priv);
|
|
#ifdef PROC_DEBUG
|
|
woal_debug_entry(priv);
|
|
#endif /* PROC_DEBUG */
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
LEAVE();
|
|
return priv;
|
|
error:
|
|
handle->priv_num = bss_index;
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
/* Unregister wiphy device and free */
|
|
if (priv) {
|
|
if (priv->wdev && IS_STA_OR_UAP_CFG80211(cfg80211_wext))
|
|
priv->wdev = NULL;
|
|
#ifdef UAP_CFG80211
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
if (priv->csa_workqueue) {
|
|
destroy_workqueue(priv->csa_workqueue);
|
|
priv->csa_workqueue = NULL;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
if (dev && dev->reg_state == NETREG_REGISTERED)
|
|
unregister_netdev(dev);
|
|
if (dev)
|
|
free_netdev(dev);
|
|
LEAVE();
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief This function removes an interface.
|
|
*
|
|
* @param handle A pointer to the moal_handle structure
|
|
* @param bss_index BSS index number
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_remove_interface(moal_handle *handle, t_u8 bss_index)
|
|
{
|
|
struct net_device *dev = NULL;
|
|
moal_private *priv = handle->priv[bss_index];
|
|
#if defined(STA_WEXT) || defined(UAP_WEXT)
|
|
union iwreq_data wrqu;
|
|
#endif
|
|
int i = 0;
|
|
|
|
ENTER();
|
|
if (!priv || !priv->netdev)
|
|
goto error;
|
|
dev = priv->netdev;
|
|
|
|
if (priv->media_connected == MTRUE) {
|
|
priv->media_connected = MFALSE;
|
|
#if defined(STA_WEXT) || defined(UAP_WEXT)
|
|
if (IS_STA_OR_UAP_WEXT(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);
|
|
|
|
if (priv->bss_type == MLAN_BSS_TYPE_STA)
|
|
woal_flush_tdls_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 ||
|
|
priv->bss_type == MLAN_BSS_TYPE_UAP) {
|
|
for (i = 0; i < handle->histogram_table_num; i++) {
|
|
kfree(priv->hist_data[i]);
|
|
priv->hist_data[i] = NULL;
|
|
}
|
|
}
|
|
#ifdef CONFIG_PROC_FS
|
|
#ifdef PROC_DEBUG
|
|
/* Remove proc debug */
|
|
woal_debug_remove(priv);
|
|
#endif /* PROC_DEBUG */
|
|
woal_proc_remove(priv);
|
|
#endif /* CONFIG_PROC_FS */
|
|
/* Last reference is our one */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
|
|
PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt));
|
|
#else
|
|
PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev));
|
|
#endif
|
|
|
|
PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name);
|
|
|
|
if (dev->reg_state == NETREG_REGISTERED)
|
|
unregister_netdev(dev);
|
|
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
/* Unregister wiphy device and free */
|
|
if (priv->wdev && IS_STA_OR_UAP_CFG80211(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
|
|
|
|
/* Clear the priv in handle */
|
|
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 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
|
|
*/
|
|
static 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 cancel all works in the queue
|
|
* and destroy the main workqueue.
|
|
*
|
|
* @param handle A pointer to moal_handle
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static 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;
|
|
}
|
|
LEAVE();
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
|
|
/**
|
|
* @brief Function to process pre/post PCIe function level reset
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param prepare True :- its a pre FLR call from the kernel
|
|
* False :- its a post FLR call from the kernel
|
|
*
|
|
* Note: This function is mix of woal_switch_drv_mode() and
|
|
* remove_card(). Idea is to cleanup the software only without
|
|
* touching the PCIe specific code. Likewise, during init init
|
|
* everything, including hw, but do not reinitiate PCIe stack
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status
|
|
woal_do_flr(moal_handle *handle, bool prepare)
|
|
{
|
|
unsigned int i;
|
|
int index = 0;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
moal_private *priv = NULL;
|
|
void *card;
|
|
int fw_serial_bkp;
|
|
|
|
ENTER();
|
|
|
|
if (!handle) {
|
|
PRINTM(MINFO, "\n Handle null during prepare=%d\n", prepare);
|
|
}
|
|
|
|
if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem))
|
|
goto exit_sem_err;
|
|
|
|
if (!prepare)
|
|
goto perform_init;
|
|
|
|
/* Reset all interfaces */
|
|
priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
|
|
woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE);
|
|
|
|
/* Shutdown firmware */
|
|
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);
|
|
|
|
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);
|
|
|
|
/* Remove interface */
|
|
for (i = 0; i < handle->priv_num; i++)
|
|
woal_remove_interface(handle, i);
|
|
|
|
/* Unregister mlan */
|
|
if (handle->pmlan_adapter) {
|
|
mlan_unregister(handle->pmlan_adapter);
|
|
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));
|
|
}
|
|
if (atomic_read(&handle->malloc_cons_count)) {
|
|
PRINTM(MERROR,
|
|
"mlan has memory leak: malloc_cons_count=%d\n",
|
|
atomic_read(&handle->malloc_cons_count));
|
|
}
|
|
handle->pmlan_adapter = NULL;
|
|
}
|
|
|
|
goto exit;
|
|
|
|
perform_init:
|
|
handle->priv_num = 0;
|
|
|
|
/* Init SW */
|
|
if (woal_init_sw(handle)) {
|
|
PRINTM(MFATAL, "Software Init Failed\n");
|
|
goto err_init_fw;
|
|
}
|
|
|
|
/* Update pcie_int_mode in mlan adapter */
|
|
mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode);
|
|
|
|
/* Init FW and HW */
|
|
/* Load wlan only binary */
|
|
fw_serial_bkp = fw_serial;
|
|
fw_serial = 0;
|
|
woal_update_firmware_name(handle);
|
|
if (woal_init_fw(handle)) {
|
|
PRINTM(MFATAL, "Firmware Init Failed\n");
|
|
woal_pcie_reg_dbg(handle);
|
|
fw_serial = fw_serial_bkp;
|
|
goto err_init_fw;
|
|
}
|
|
fw_serial = fw_serial_bkp;
|
|
exit:
|
|
MOAL_REL_SEMAPHORE(&AddRemoveCardSem);
|
|
|
|
exit_sem_err:
|
|
LEAVE();
|
|
return status;
|
|
|
|
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
|
|
#ifdef CONFIG_PROC_FS
|
|
woal_proc_exit(handle);
|
|
#endif
|
|
/* Unregister device */
|
|
PRINTM(MINFO, "unregister device\n");
|
|
woal_unregister_dev(handle);
|
|
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 */
|
|
woal_terminate_workqueue(handle);
|
|
woal_free_moal_handle(handle);
|
|
|
|
for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
|
|
if (m_handle[index] == handle)
|
|
break;
|
|
}
|
|
if (index < MAX_MLAN_ADAPTER)
|
|
m_handle[index] = NULL;
|
|
card = handle->card;
|
|
((pcie_service_card *)card)->handle = NULL;
|
|
MOAL_REL_SEMAPHORE(&AddRemoveCardSem);
|
|
LEAVE();
|
|
return (mlan_status)MLAN_STATUS_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
/********************************************************
|
|
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);
|
|
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(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. */
|
|
{
|
|
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 defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
if (!p2p_enh) {
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
|
|
IS_STA_CFG80211(cfg80211_wext)) {
|
|
priv->phandle->wiphy->interface_modes |=
|
|
MBIT(NL80211_IFTYPE_P2P_GO) |
|
|
MBIT(NL80211_IFTYPE_P2P_CLIENT);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#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);
|
|
|
|
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)
|
|
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);
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
if (!p2p_enh) {
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
|
|
!priv->bss_virtual &&
|
|
IS_STA_CFG80211(cfg80211_wext) &&
|
|
IS_UAP_CFG80211(cfg80211_wext)) {
|
|
priv->phandle->wiphy->interface_modes &=
|
|
~(MBIT(NL80211_IFTYPE_P2P_GO) |
|
|
MBIT(NL80211_IFTYPE_P2P_CLIENT));
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
MODULE_PUT;
|
|
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief This function sets the MAC address to firmware.
|
|
*
|
|
* @param dev A pointer to mlan_private structure
|
|
* @param addr MAC address to set
|
|
*
|
|
* @return 0 --success, otherwise fail
|
|
*/
|
|
int
|
|
woal_set_mac_address(struct net_device *dev, void *addr)
|
|
{
|
|
int ret = 0;
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
struct sockaddr *phw_addr = (struct sockaddr *)addr;
|
|
t_u8 prev_addr[ETH_ALEN];
|
|
|
|
ENTER();
|
|
|
|
if (priv->phandle->surprise_removed == MTRUE) {
|
|
PRINTM(MERROR,
|
|
"Set mac address is not allowed in surprise remove state.\n");
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
|
|
memcpy(prev_addr, priv->current_addr, 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);
|
|
memcpy(priv->current_addr, phw_addr->sa_data, ETH_ALEN);
|
|
#if defined(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)) {
|
|
PRINTM(MERROR, "Set MAC address failed\n");
|
|
/* For failure restore the MAC address */
|
|
memcpy(priv->current_addr, prev_addr, ETH_ALEN);
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN);
|
|
memcpy(dev->dev_addr, priv->current_addr, ETH_ALEN);
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Check driver status
|
|
*
|
|
* @param handle A pointer to moal_handle
|
|
*
|
|
* @return MTRUE/MFALSE
|
|
*/
|
|
t_u8
|
|
woal_check_driver_status(moal_handle *handle)
|
|
{
|
|
moal_private *priv = NULL;
|
|
struct timeval t;
|
|
int i = 0;
|
|
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.pending_cmd &&
|
|
(t.tv_sec > (info.dnld_cmd_in_secs + MOAL_CMD_TIMEOUT_MAX))) {
|
|
if (t.tv_sec > (info.dnld_cmd_in_secs + MOAL_CMD_TIMEOUT) &&
|
|
!info.num_cmd_timeout) {
|
|
PRINTM(MERROR, "Ignore invalid time, wait=%d\n",
|
|
(int)(t.tv_sec - info.dnld_cmd_in_secs));
|
|
} else {
|
|
PRINTM(MERROR, "Timeout cmd id = 0x%x wait=%d\n",
|
|
info.pending_cmd,
|
|
(int)(t.tv_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.tv_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.tv_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->phandle->driver_state) {
|
|
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;
|
|
char str[512] = { 0 };
|
|
char *s;
|
|
|
|
ENTER();
|
|
|
|
if (!priv || 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, "num_cmd_timeout = %d\n", info.num_cmd_timeout);
|
|
PRINTM(MERROR, "dbg.num_cmd_timeout = %d\n", info.dbg_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);
|
|
|
|
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, "cmd_resp_received = %d\n", info.cmd_resp_received);
|
|
PRINTM(MERROR, "event_received = %d\n", info.event_received);
|
|
|
|
PRINTM(MERROR, "max_tx_buf_size = %d\n", info.max_tx_buf_size);
|
|
PRINTM(MERROR, "tx_buf_size = %d\n", info.tx_buf_size);
|
|
PRINTM(MERROR, "curr_tx_buf_size = %d\n", info.curr_tx_buf_size);
|
|
|
|
PRINTM(MERROR, "data_sent=%d cmd_sent=%d\n", info.data_sent,
|
|
info.cmd_sent);
|
|
|
|
PRINTM(MERROR, "ps_mode=%d ps_state=%d\n", info.ps_mode, info.ps_state);
|
|
PRINTM(MERROR, "wakeup_dev_req=%d wakeup_tries=%d\n",
|
|
info.pm_wakeup_card_req, info.pm_wakeup_fw_try);
|
|
PRINTM(MERROR, "hs_configured=%d hs_activated=%d\n",
|
|
info.is_hs_configured, info.hs_activated);
|
|
PRINTM(MERROR, "pps_uapsd_mode=%d sleep_pd=%d\n",
|
|
info.pps_uapsd_mode, info.sleep_pd);
|
|
PRINTM(MERROR, "tx_lock_flag = %d\n", info.tx_lock_flag);
|
|
PRINTM(MERROR, "port_open = %d\n", info.port_open);
|
|
PRINTM(MERROR, "scan_processing = %d\n", info.scan_processing);
|
|
|
|
for (i = 0; i < info.ralist_num; i++) {
|
|
PRINTM(MERROR,
|
|
"ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n",
|
|
info.ralist[i].ra[0], info.ralist[i].ra[1],
|
|
info.ralist[i].ra[2], info.ralist[i].ra[3],
|
|
info.ralist[i].ra[4], info.ralist[i].ra[5],
|
|
info.ralist[i].tid, info.ralist[i].total_pkts,
|
|
info.ralist[i].tx_pause);
|
|
}
|
|
|
|
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);
|
|
PRINTM(MERROR, "------------mlan_debug_info End-------------\n");
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function handle the shutdown timeout issue
|
|
*
|
|
* @param handle Pointer to structure moal_handle
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_ioctl_timeout(moal_handle *handle)
|
|
{
|
|
moal_private *priv = NULL;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MMSG, "woal_ioctl_timout.\n");
|
|
priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
|
|
if (priv) {
|
|
woal_mlan_debug_info(priv);
|
|
woal_moal_debug_info(priv, NULL, MFALSE);
|
|
}
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles the timeout of packet
|
|
* transmission
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_tx_timeout(struct net_device *dev)
|
|
{
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
|
|
ENTER();
|
|
|
|
priv->num_tx_timeout++;
|
|
PRINTM(MERROR, "%lu : %s (bss=%d): Tx timeout (%d)\n",
|
|
jiffies, dev->name, priv->bss_index, priv->num_tx_timeout);
|
|
woal_set_trans_start(dev);
|
|
|
|
if (priv->num_tx_timeout == NUM_TX_TIMEOUT_THRESHOLD) {
|
|
woal_mlan_debug_info(priv);
|
|
woal_moal_debug_info(priv, NULL, MFALSE);
|
|
woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG,
|
|
strlen(CUS_EVT_DRIVER_HANG));
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
if (IS_STA_OR_UAP_CFG80211(cfg80211_wext))
|
|
woal_cfg80211_vendor_event(priv, event_hang,
|
|
CUS_EVT_DRIVER_HANG,
|
|
strlen(CUS_EVT_DRIVER_HANG));
|
|
#endif
|
|
#endif
|
|
priv->phandle->driver_state = MTRUE;
|
|
woal_process_hang(priv->phandle);
|
|
}
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function returns the network statistics
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
*
|
|
* @return A pointer to net_device_stats structure
|
|
*/
|
|
struct net_device_stats *
|
|
woal_get_stats(struct net_device *dev)
|
|
{
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
return &priv->stats;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
/**
|
|
* @brief This function handles wmm queue select
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
* @param skb A pointer to sk_buff structure
|
|
*
|
|
* @return tx_queue index (0-3)
|
|
*/
|
|
u16
|
|
woal_select_queue(struct net_device *dev, struct sk_buff *skb
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
|
, struct net_device *sb_dev
|
|
#else
|
|
, void *accel_priv
|
|
#endif
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0))
|
|
, select_queue_fallback_t fallback
|
|
#endif
|
|
#endif
|
|
)
|
|
{
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
struct ethhdr *eth = NULL;
|
|
t_u8 tid = 0;
|
|
t_u8 index = 0;
|
|
|
|
ENTER();
|
|
|
|
/*
|
|
* skb->priority values from 256->263 are magic values to
|
|
* directly indicate a specific 802.1d priority. This is used
|
|
* to allow 802.1d priority to be passed directly in from VLAN
|
|
* tags, etc.
|
|
*/
|
|
if (IS_SKB_MAGIC_VLAN(skb)) {
|
|
tid = GET_VLAN_PRIO(skb);
|
|
} else {
|
|
eth = (struct ethhdr *)skb->data;
|
|
switch (eth->h_proto) {
|
|
case __constant_htons(ETH_P_IP):
|
|
tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET];
|
|
if (tid == 0xFF)
|
|
tid = (IPTOS_PREC(SKB_TOS(skb)) >>
|
|
IPTOS_OFFSET);
|
|
break;
|
|
case __constant_htons(ETH_P_IPV6):
|
|
tid = SKB_TIDV6(skb);
|
|
break;
|
|
case __constant_htons(ETH_P_ARP):
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
index = mlan_select_wmm_queue(priv->phandle->pmlan_adapter,
|
|
priv->bss_index, tid);
|
|
PRINTM(MDATA, "select queue: tid=%d, index=%d\n", tid, index);
|
|
LEAVE();
|
|
return index;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function flush tx status queue
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_flush_tx_stat_queue(moal_private *priv)
|
|
{
|
|
struct tx_status_info *tx_info = NULL, *tmp_node;
|
|
unsigned long flags;
|
|
struct sk_buff *skb = NULL;
|
|
spin_lock_irqsave(&priv->tx_stat_lock, flags);
|
|
list_for_each_entry_safe(tx_info, tmp_node, &priv->tx_stat_queue, link) {
|
|
list_del(&tx_info->link);
|
|
spin_unlock_irqrestore(&priv->tx_stat_lock, flags);
|
|
skb = (struct sk_buff *)tx_info->tx_skb;
|
|
if (tx_info->tx_cookie) {
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
cfg80211_mgmt_tx_status(priv->netdev,
|
|
tx_info->tx_cookie, skb->data,
|
|
skb->len, true, GFP_ATOMIC);
|
|
#else
|
|
cfg80211_mgmt_tx_status(priv->wdev, tx_info->tx_cookie,
|
|
skb->data, skb->len, true,
|
|
GFP_ATOMIC);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
dev_kfree_skb_any(skb);
|
|
kfree(tx_info);
|
|
spin_lock_irqsave(&priv->tx_stat_lock, flags);
|
|
}
|
|
INIT_LIST_HEAD(&priv->tx_stat_queue);
|
|
spin_unlock_irqrestore(&priv->tx_stat_lock, flags);
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets tx info from tx_stat_queue
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param tx_seq_num tx seq number
|
|
*
|
|
* @return A pointer to the tcp tx_status_info structure, if found.
|
|
* Otherwise, null
|
|
*/
|
|
struct tx_status_info *
|
|
woal_get_tx_info(moal_private *priv, t_u8 tx_seq_num)
|
|
{
|
|
struct tx_status_info *tx_info = NULL;
|
|
ENTER();
|
|
|
|
list_for_each_entry(tx_info, &priv->tx_stat_queue, link) {
|
|
if (tx_info->tx_seq_num == tx_seq_num) {
|
|
LEAVE();
|
|
return tx_info;
|
|
}
|
|
}
|
|
LEAVE();
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief This function remove tx info from queue
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param tx_seq_num tx seq number
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_remove_tx_info(moal_private *priv, t_u8 tx_seq_num)
|
|
{
|
|
struct tx_status_info *tx_info, *tmp = NULL;
|
|
unsigned long flags;
|
|
ENTER();
|
|
|
|
spin_lock_irqsave(&priv->tx_stat_lock, flags);
|
|
list_for_each_entry_safe(tx_info, tmp, &priv->tx_stat_queue, link) {
|
|
if (tx_info->tx_seq_num == tx_seq_num) {
|
|
list_del(&tx_info->link);
|
|
dev_kfree_skb_any((struct sk_buff *)tx_info->tx_skb);
|
|
kfree(tx_info);
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&priv->tx_stat_lock, flags);
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function flush tcp session queue
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_flush_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
|
|
*/
|
|
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();
|
|
memcpy(ra, skb->data, MLAN_MAC_ADDR_LENGTH);
|
|
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
|
|
|
|
/**
|
|
* @brief This function flush tcp session queue
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_flush_tcp_sess_queue(moal_private *priv)
|
|
{
|
|
struct tcp_sess *tcp_sess = NULL, *tmp_node;
|
|
unsigned long flags;
|
|
struct sk_buff *skb;
|
|
spin_lock_irqsave(&priv->tcp_sess_lock, flags);
|
|
list_for_each_entry_safe(tcp_sess, tmp_node, &priv->tcp_sess_queue,
|
|
link) {
|
|
list_del(&tcp_sess->link);
|
|
if (tcp_sess->is_timer_set)
|
|
woal_cancel_timer(&tcp_sess->ack_timer);
|
|
skb = (struct sk_buff *)tcp_sess->ack_skb;
|
|
if (skb)
|
|
dev_kfree_skb_any(skb);
|
|
kfree(tcp_sess);
|
|
}
|
|
INIT_LIST_HEAD(&priv->tcp_sess_queue);
|
|
priv->tcp_ack_drop_cnt = 0;
|
|
priv->tcp_ack_cnt = 0;
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock, flags);
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets tcp session from the tcp session queue
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param src_ip IP address of the device
|
|
* @param src_port TCP port of the device
|
|
* @param dst_ip IP address of the client
|
|
* @param dst_port TCP port of the client
|
|
*
|
|
* @return A pointer to the tcp session data structure, if found.
|
|
* Otherwise, null
|
|
*/
|
|
static inline struct tcp_sess *
|
|
woal_get_tcp_sess(moal_private *priv,
|
|
t_u32 src_ip, t_u16 src_port, t_u32 dst_ip, t_u16 dst_port)
|
|
{
|
|
struct tcp_sess *tcp_sess = NULL;
|
|
ENTER();
|
|
|
|
list_for_each_entry(tcp_sess, &priv->tcp_sess_queue, link) {
|
|
if ((tcp_sess->src_ip_addr == src_ip) &&
|
|
(tcp_sess->src_tcp_port == src_port) &&
|
|
(tcp_sess->dst_ip_addr == dst_ip) &&
|
|
(tcp_sess->dst_tcp_port == dst_port)) {
|
|
LEAVE();
|
|
return tcp_sess;
|
|
}
|
|
}
|
|
LEAVE();
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief This function send the holding tcp ack packet
|
|
* re-assoc thread.
|
|
*
|
|
* @param context A pointer to context
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_tcp_ack_timer_func(void *context)
|
|
{
|
|
struct tcp_sess *tcp_session = (struct tcp_sess *)context;
|
|
moal_private *priv = (moal_private *)tcp_session->priv;
|
|
unsigned long flags;
|
|
mlan_buffer *pmbuf;
|
|
struct sk_buff *skb;
|
|
mlan_status status;
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
t_u32 index = 0;
|
|
#endif
|
|
ENTER();
|
|
spin_lock_irqsave(&priv->tcp_sess_lock, flags);
|
|
tcp_session->is_timer_set = MFALSE;
|
|
skb = (struct sk_buff *)tcp_session->ack_skb;
|
|
pmbuf = (mlan_buffer *)tcp_session->pmbuf;
|
|
tcp_session->ack_skb = NULL;
|
|
tcp_session->pmbuf = NULL;
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock, flags);
|
|
if (skb && pmbuf) {
|
|
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
|
|
switch (status) {
|
|
case MLAN_STATUS_PENDING:
|
|
atomic_inc(&priv->phandle->tx_pending);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
index = skb_get_queue_mapping(skb);
|
|
atomic_inc(&priv->wmm_tx_pending[index]);
|
|
if (atomic_read(&priv->wmm_tx_pending[index]) >=
|
|
MAX_TX_PENDING) {
|
|
struct netdev_queue *txq =
|
|
netdev_get_tx_queue(priv->netdev,
|
|
index);
|
|
netif_tx_stop_queue(txq);
|
|
PRINTM(MINFO, "Stop Kernel Queue : %d\n",
|
|
index);
|
|
}
|
|
#else
|
|
if (atomic_read(&priv->phandle->tx_pending) >=
|
|
MAX_TX_PENDING)
|
|
woal_stop_queue(priv->netdev);
|
|
#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */
|
|
queue_work(priv->phandle->workqueue,
|
|
&priv->phandle->main_work);
|
|
break;
|
|
case MLAN_STATUS_SUCCESS:
|
|
priv->stats.tx_packets++;
|
|
priv->stats.tx_bytes += skb->len;
|
|
dev_kfree_skb_any(skb);
|
|
break;
|
|
case MLAN_STATUS_FAILURE:
|
|
default:
|
|
priv->stats.tx_dropped++;
|
|
dev_kfree_skb_any(skb);
|
|
break;
|
|
}
|
|
}
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function send the tcp ack
|
|
*
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param tcp_session A pointer to tcp_session
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_send_tcp_ack(moal_private *priv, struct tcp_sess *tcp_session)
|
|
{
|
|
mlan_status status;
|
|
struct sk_buff *skb = (struct sk_buff *)tcp_session->ack_skb;
|
|
mlan_buffer *pmbuf = (mlan_buffer *)tcp_session->pmbuf;
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
t_u32 index = 0;
|
|
#endif
|
|
ENTER();
|
|
if (tcp_session->is_timer_set) {
|
|
woal_cancel_timer(&tcp_session->ack_timer);
|
|
tcp_session->is_timer_set = MFALSE;
|
|
}
|
|
tcp_session->ack_skb = NULL;
|
|
tcp_session->pmbuf = NULL;
|
|
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
|
|
switch (status) {
|
|
case MLAN_STATUS_PENDING:
|
|
atomic_inc(&priv->phandle->tx_pending);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
index = skb_get_queue_mapping(skb);
|
|
atomic_inc(&priv->wmm_tx_pending[index]);
|
|
if (atomic_read(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
|
|
struct netdev_queue *txq =
|
|
netdev_get_tx_queue(priv->netdev, index);
|
|
netif_tx_stop_queue(txq);
|
|
PRINTM(MINFO, "Stop Kernel Queue : %d\n", index);
|
|
}
|
|
#else
|
|
if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING)
|
|
woal_stop_queue(priv->netdev);
|
|
#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */
|
|
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
|
|
break;
|
|
case MLAN_STATUS_SUCCESS:
|
|
priv->stats.tx_packets++;
|
|
priv->stats.tx_bytes += skb->len;
|
|
dev_kfree_skb_any(skb);
|
|
break;
|
|
case MLAN_STATUS_FAILURE:
|
|
default:
|
|
priv->stats.tx_dropped++;
|
|
dev_kfree_skb_any(skb);
|
|
break;
|
|
}
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function get the tcp ack session node
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param pmbuf A pointer to mlan_buffer associated with a skb
|
|
*
|
|
* @return 1, if it's dropped; 0, if not dropped
|
|
*/
|
|
int
|
|
woal_process_tcp_ack(moal_private *priv, mlan_buffer *pmbuf)
|
|
{
|
|
int ret = 0;
|
|
unsigned long flags;
|
|
struct tcp_sess *tcp_session;
|
|
struct ethhdr *ethh = NULL;
|
|
struct iphdr *iph = NULL;
|
|
struct tcphdr *tcph = NULL;
|
|
t_u32 ack_seq;
|
|
struct sk_buff *skb;
|
|
|
|
ENTER();
|
|
|
|
/** check the tcp packet */
|
|
ethh = (struct ethhdr *)(pmbuf->pbuf + pmbuf->data_offset);
|
|
if (ntohs(ethh->h_proto) != ETH_P_IP) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
iph = (struct iphdr *)((t_u8 *)ethh + sizeof(struct ethhdr));
|
|
if (iph->protocol != IPPROTO_TCP) {
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
tcph = (struct tcphdr *)((t_u8 *)iph + iph->ihl * 4);
|
|
|
|
if (*((t_u8 *)tcph + 13) == 0x10) {
|
|
/* Only replace ACK */
|
|
if (ntohs(iph->tot_len) > (iph->ihl + tcph->doff) * 4) {
|
|
/* Don't drop ACK with payload */
|
|
/* TODO: should we delete previous TCP session */
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
priv->tcp_ack_cnt++;
|
|
spin_lock_irqsave(&priv->tcp_sess_lock, flags);
|
|
tcp_session = woal_get_tcp_sess(priv, iph->saddr,
|
|
tcph->source, iph->daddr,
|
|
tcph->dest);
|
|
if (!tcp_session) {
|
|
tcp_session =
|
|
kmalloc(sizeof(struct tcp_sess), GFP_ATOMIC);
|
|
if (!tcp_session) {
|
|
PRINTM(MERROR, "Fail to allocate tcp_sess.\n");
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock,
|
|
flags);
|
|
goto done;
|
|
}
|
|
tcp_session->ack_skb = pmbuf->pdesc;
|
|
tcp_session->pmbuf = pmbuf;
|
|
pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK;
|
|
tcp_session->src_ip_addr = iph->saddr;
|
|
tcp_session->dst_ip_addr = iph->daddr;
|
|
tcp_session->src_tcp_port = tcph->source;
|
|
tcp_session->dst_tcp_port = tcph->dest;
|
|
tcp_session->ack_seq = ntohl(tcph->ack_seq);
|
|
tcp_session->priv = (void *)priv;
|
|
skb = (struct sk_buff *)pmbuf->pdesc;
|
|
skb->cb[0] = 0;
|
|
/* Initialize the timer for tcp ack */
|
|
woal_initialize_timer(&tcp_session->ack_timer,
|
|
woal_tcp_ack_timer_func,
|
|
tcp_session);
|
|
tcp_session->is_timer_set = MTRUE;
|
|
woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS);
|
|
list_add_tail(&tcp_session->link,
|
|
&priv->tcp_sess_queue);
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock, flags);
|
|
ret = HOLD_TCP_ACK;
|
|
LEAVE();
|
|
return ret;
|
|
} else if (!tcp_session->ack_skb) {
|
|
tcp_session->ack_skb = pmbuf->pdesc;
|
|
tcp_session->pmbuf = pmbuf;
|
|
pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK;
|
|
tcp_session->ack_seq = ntohl(tcph->ack_seq);
|
|
tcp_session->priv = (void *)priv;
|
|
skb = (struct sk_buff *)pmbuf->pdesc;
|
|
skb->cb[0] = 0;
|
|
tcp_session->is_timer_set = MTRUE;
|
|
woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS);
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock, flags);
|
|
ret = HOLD_TCP_ACK;
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
ack_seq = ntohl(tcph->ack_seq);
|
|
skb = (struct sk_buff *)tcp_session->ack_skb;
|
|
if (likely(ack_seq > tcp_session->ack_seq) &&
|
|
(skb->len == pmbuf->data_len)) {
|
|
memcpy(skb->data, pmbuf->pbuf + pmbuf->data_offset,
|
|
pmbuf->data_len);
|
|
tcp_session->ack_seq = ack_seq;
|
|
ret = DROP_TCP_ACK;
|
|
skb->cb[0]++;
|
|
//We will drop 90% tcp ack
|
|
#define TCP_ACK_MAX_HOLD 9
|
|
if (skb->cb[0] >= TCP_ACK_MAX_HOLD)
|
|
woal_send_tcp_ack(priv, tcp_session);
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock, flags);
|
|
skb = (struct sk_buff *)pmbuf->pdesc;
|
|
dev_kfree_skb_any(skb);
|
|
priv->tcp_ack_drop_cnt++;
|
|
} else {
|
|
pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK;
|
|
spin_unlock_irqrestore(&priv->tcp_sess_lock, flags);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
}
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function handles packet transmission
|
|
*
|
|
* @param skb A pointer to sk_buff structure
|
|
* @param dev A pointer to net_device structure
|
|
*
|
|
* @return 0 --success
|
|
*/
|
|
int
|
|
woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
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();
|
|
PRINTM(MDATA, "%lu : %s (bss=%d): Data <= kernel\n",
|
|
jiffies, dev->name, priv->bss_index);
|
|
|
|
if (priv->phandle->surprise_removed == MTRUE) {
|
|
dev_kfree_skb_any(skb);
|
|
priv->stats.tx_dropped++;
|
|
goto done;
|
|
}
|
|
priv->num_tx_timeout = 0;
|
|
if (!skb->len || (skb->len > ETH_FRAME_LEN)) {
|
|
PRINTM(MERROR, "Tx Error: Bad skb length %d : %d\n",
|
|
skb->len, ETH_FRAME_LEN);
|
|
dev_kfree_skb_any(skb);
|
|
priv->stats.tx_dropped++;
|
|
goto done;
|
|
}
|
|
if (skb->cloned || (skb_headroom(skb) < (MLAN_MIN_DATA_HEADER_LEN +
|
|
sizeof(mlan_buffer) +
|
|
priv->extra_tx_head_len))) {
|
|
PRINTM(MWARN,
|
|
"Tx: skb cloned %d or Insufficient skb headroom %d\n",
|
|
skb->cloned, skb_headroom(skb));
|
|
/* Insufficient skb headroom - allocate a new skb */
|
|
new_skb = skb_realloc_headroom(skb, MLAN_MIN_DATA_HEADER_LEN +
|
|
sizeof(mlan_buffer) +
|
|
priv->extra_tx_head_len);
|
|
if (unlikely(!new_skb)) {
|
|
PRINTM(MERROR, "Tx: Cannot allocate skb\n");
|
|
dev_kfree_skb_any(skb);
|
|
priv->stats.tx_dropped++;
|
|
goto done;
|
|
}
|
|
if (new_skb != skb)
|
|
dev_kfree_skb_any(skb);
|
|
skb = new_skb;
|
|
PRINTM(MINFO, "new skb headroom %d\n", skb_headroom(skb));
|
|
}
|
|
pmbuf = (mlan_buffer *)skb->head;
|
|
memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer));
|
|
pmbuf->bss_index = priv->bss_index;
|
|
woal_fill_mlan_buffer(priv, pmbuf, skb);
|
|
if (priv->enable_tcp_ack_enh == MTRUE) {
|
|
ret = woal_process_tcp_ack(priv, pmbuf);
|
|
if (ret)
|
|
goto done;
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
if (priv->enable_auto_tdls && priv->tdls_check_tx)
|
|
woal_tdls_check_tx(priv, skb);
|
|
#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:
|
|
atomic_inc(&priv->phandle->tx_pending);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
atomic_inc(&priv->wmm_tx_pending[index]);
|
|
if (atomic_read(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
|
|
struct netdev_queue *txq =
|
|
netdev_get_tx_queue(priv->netdev, index);
|
|
netif_tx_stop_queue(txq);
|
|
PRINTM(MINFO, "Stop Kernel Queue : %d\n", index);
|
|
}
|
|
#else
|
|
if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING)
|
|
woal_stop_queue(priv->netdev);
|
|
#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */
|
|
if (!mlan_is_main_process_running(priv->phandle->pmlan_adapter))
|
|
queue_work(priv->phandle->workqueue,
|
|
&priv->phandle->main_work);
|
|
break;
|
|
case MLAN_STATUS_SUCCESS:
|
|
priv->stats.tx_packets++;
|
|
priv->stats.tx_bytes += skb->len;
|
|
dev_kfree_skb_any(skb);
|
|
break;
|
|
case MLAN_STATUS_FAILURE:
|
|
default:
|
|
priv->stats.tx_dropped++;
|
|
dev_kfree_skb_any(skb);
|
|
break;
|
|
}
|
|
done:
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 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;
|
|
}
|
|
memcpy(mac_buff, buf, strlen(buf));
|
|
|
|
begin = mac_buff;
|
|
for (i = 0; i < ETH_ALEN; ++i) {
|
|
end = woal_strsep(&begin, ':', '/');
|
|
if (end)
|
|
mac_addr[i] = woal_atox(end);
|
|
}
|
|
|
|
kfree(mac_buff);
|
|
LEAVE();
|
|
}
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief This function sets multicast addresses to firmware
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_set_multicast_list(struct net_device *dev)
|
|
{
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
ENTER();
|
|
woal_request_set_multicast_list(priv, dev);
|
|
LEAVE();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function initializes the private structure
|
|
* and set default value to the member of moal_private.
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param wait_option Wait option
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_init_priv(moal_private *priv, t_u8 wait_option)
|
|
{
|
|
ENTER();
|
|
#ifdef STA_SUPPORT
|
|
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
|
|
priv->current_key_index = 0;
|
|
priv->rate_index = AUTO_RATE;
|
|
priv->is_adhoc_link_sensed = MFALSE;
|
|
priv->scan_type = MLAN_SCAN_TYPE_ACTIVE;
|
|
priv->bg_scan_start = MFALSE;
|
|
priv->bg_scan_reported = MFALSE;
|
|
memset(&priv->nick_name, 0, sizeof(priv->nick_name));
|
|
priv->num_tx_timeout = 0;
|
|
priv->rx_filter = 0;
|
|
|
|
#ifdef REASSOCIATION
|
|
priv->reassoc_on = MFALSE;
|
|
priv->set_asynced_essid_flag = MFALSE;
|
|
#endif
|
|
#ifdef STA_CFG80211
|
|
memset(&priv->sme_current, 0,
|
|
sizeof(struct cfg80211_connect_params));
|
|
#endif
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
#ifdef UAP_SUPPORT
|
|
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
|
|
priv->bss_started = MFALSE;
|
|
#ifdef UAP_CFG80211
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def));
|
|
memset(&priv->csa_chan, 0, sizeof(struct cfg80211_chan_def));
|
|
priv->uap_tx_blocked = MFALSE;
|
|
memset(&priv->beacon_after, 0,
|
|
sizeof(struct cfg80211_beacon_data));
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
memset(&priv->tx_protocols, 0, sizeof(dot11_protocol));
|
|
memset(&priv->rx_protocols, 0, sizeof(dot11_protocol));
|
|
priv->media_connected = MFALSE;
|
|
|
|
memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map));
|
|
|
|
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
|
|
priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
priv->beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK;
|
|
#endif
|
|
|
|
#ifdef STA_SUPPORT
|
|
priv->pmk_saved = MFALSE;
|
|
memset(&priv->pmk, 0, sizeof(mlan_pmk_t));
|
|
#endif
|
|
|
|
priv->enable_tcp_ack_enh = MTRUE;
|
|
|
|
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) {
|
|
memset(priv->current_addr, 0, ETH_ALEN);
|
|
memcpy(priv->current_addr, priv->phandle->mac_addr, ETH_ALEN);
|
|
}
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
#ifdef MFG_CMD_SUPPORT
|
|
if (mfg_mode != MLAN_INIT_PARA_ENABLED)
|
|
#endif
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
|
|
if (priv->bss_virtual) {
|
|
if (priv->pa_netdev) {
|
|
memcpy(priv->current_addr,
|
|
priv->pa_netdev->dev_addr,
|
|
ETH_ALEN);
|
|
priv->current_addr[4] ^= 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 and let them different with each other */
|
|
if (priv->bss_index > 0
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
&& priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT
|
|
#endif
|
|
) {
|
|
priv->current_addr[4] += priv->bss_index;
|
|
PRINTM(MCMND, "Set %s device addr: " MACSTR "\n",
|
|
priv->netdev->name, MAC2STR(priv->current_addr));
|
|
}
|
|
|
|
/* Set MAC address to fw */
|
|
woal_request_set_mac_address(priv);
|
|
memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN);
|
|
|
|
#ifdef UAP_SUPPORT
|
|
#if defined(DFS_TESTING_SUPPORT)
|
|
priv->user_cac_period_msec = 0;
|
|
#endif
|
|
#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;
|
|
|
|
/* 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);
|
|
|
|
#ifdef STA_SUPPORT
|
|
woal_cancel_scan(priv, wait_option);
|
|
#endif
|
|
|
|
/* 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 defined(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;
|
|
}
|
|
|
|
#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);
|
|
memcpy(NLMSG_DATA(nlh), netdev->name, IFNAMSIZ);
|
|
memcpy(((t_u8 *)(NLMSG_DATA(nlh))) + IFNAMSIZ, payload, len);
|
|
|
|
/* From Kernel */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
|
|
NETLINK_CB(skb).pid = 0;
|
|
#else
|
|
NETLINK_CB(skb).portid = 0;
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
|
/* Multicast message */
|
|
NETLINK_CB(skb).dst_pid = 0;
|
|
#endif
|
|
|
|
/* Multicast group number */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
|
|
NETLINK_CB(skb).dst_groups = NL_MULTICAST_GROUP;
|
|
#else
|
|
NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP;
|
|
#endif
|
|
|
|
/* Send message */
|
|
ret = netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP,
|
|
GFP_ATOMIC);
|
|
if (ret) {
|
|
PRINTM(MWARN, "netlink_broadcast failed: ret=%d\n",
|
|
ret);
|
|
goto done;
|
|
}
|
|
|
|
ret = MLAN_STATUS_SUCCESS;
|
|
} else {
|
|
PRINTM(MERROR,
|
|
"Could not send event through NETLINK. Link down.\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
}
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#ifdef REASSOCIATION
|
|
/**
|
|
* @brief This function handles re-association. it is triggered
|
|
* by re-assoc timer.
|
|
*
|
|
* @param data A pointer to wlan_thread structure
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
int
|
|
woal_reassociation_thread(void *data)
|
|
{
|
|
moal_thread *pmoal_thread = data;
|
|
moal_private *priv = NULL;
|
|
moal_handle *handle = (moal_handle *)pmoal_thread->handle;
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
|
|
wait_queue_t wait;
|
|
#else
|
|
wait_queue_entry_t wait;
|
|
#endif
|
|
int i;
|
|
BOOLEAN reassoc_timer_req;
|
|
mlan_802_11_ssid req_ssid;
|
|
mlan_ssid_bssid ssid_bssid;
|
|
mlan_status status;
|
|
mlan_bss_info bss_info;
|
|
t_u32 timer_val = MOAL_TIMER_10S;
|
|
t_u8 zero_mac[] = { 0, 0, 0, 0, 0, 0 };
|
|
ENTER();
|
|
|
|
woal_activate_thread(pmoal_thread);
|
|
init_waitqueue_entry(&wait, current);
|
|
|
|
current->flags |= PF_NOFREEZE;
|
|
|
|
for (;;) {
|
|
add_wait_queue(&pmoal_thread->wait_q, &wait);
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
schedule();
|
|
|
|
set_current_state(TASK_RUNNING);
|
|
remove_wait_queue(&pmoal_thread->wait_q, &wait);
|
|
|
|
/* 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));
|
|
memcpy(&req_ssid,
|
|
&priv->prev_ssid_bssid.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");
|
|
memcpy(&ssid_bssid.bssid,
|
|
&priv->prev_ssid_bssid.bssid,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
} else {
|
|
/* Search AP by ESSID for asynced essid setting */
|
|
PRINTM(MINFO,
|
|
"Set asynced essid: Search AP by ESSID\n");
|
|
}
|
|
|
|
memcpy(&ssid_bssid.ssid,
|
|
&priv->prev_ssid_bssid.ssid,
|
|
sizeof(mlan_802_11_ssid));
|
|
} else {
|
|
/* Search AP by BSSID first */
|
|
PRINTM(MINFO,
|
|
"Reassoc: Search AP by BSSID first\n");
|
|
memcpy(&ssid_bssid.bssid,
|
|
&priv->prev_ssid_bssid.bssid,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
}
|
|
|
|
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));
|
|
memcpy(&ssid_bssid.ssid,
|
|
&priv->prev_ssid_bssid.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 {
|
|
memcpy(&priv->prev_ssid_bssid.
|
|
ssid, &bss_info.ssid,
|
|
sizeof
|
|
(mlan_802_11_ssid));
|
|
memcpy(&priv->prev_ssid_bssid.
|
|
bssid, &bss_info.bssid,
|
|
MLAN_MAC_ADDR_LENGTH);
|
|
}
|
|
priv->set_asynced_essid_flag = MFALSE;
|
|
}
|
|
if (priv->rate_index != AUTO_RATE) {
|
|
req = woal_alloc_mlan_ioctl_req(sizeof
|
|
(mlan_ds_rate));
|
|
|
|
if (req == NULL) {
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
|
|
rate = (mlan_ds_rate *)req->pbuf;
|
|
rate->param.rate_cfg.rate_type =
|
|
MLAN_RATE_INDEX;
|
|
rate->sub_command = MLAN_OID_RATE_CFG;
|
|
req->req_id = MLAN_IOCTL_RATE;
|
|
|
|
req->action = MLAN_ACT_SET;
|
|
|
|
rate->param.rate_cfg.rate =
|
|
priv->rate_index;
|
|
|
|
status = woal_request_ioctl(priv, req,
|
|
MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
if (status !=
|
|
MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
kfree(req);
|
|
}
|
|
}
|
|
|
|
MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
|
|
}
|
|
|
|
if (handle->surprise_removed)
|
|
break;
|
|
|
|
if (reassoc_timer_req == MTRUE) {
|
|
handle->is_reassoc_timer_set = MTRUE;
|
|
if (priv && (priv->set_asynced_essid_flag == MTRUE)) {
|
|
PRINTM(MERROR,
|
|
"Set Async ESSID: No AP found or assoc failed.\n");
|
|
priv->set_asynced_essid_flag = MFALSE;
|
|
} else {
|
|
PRINTM(MEVENT,
|
|
"Reassoc: No AP found or assoc failed. "
|
|
"Restarting re-assoc Timer: %d\n",
|
|
(int)timer_val);
|
|
woal_mod_timer(&handle->reassoc_timer,
|
|
timer_val);
|
|
}
|
|
} else {
|
|
if (priv) {
|
|
priv->set_asynced_essid_flag = MFALSE;
|
|
}
|
|
}
|
|
}
|
|
woal_deactivate_thread(pmoal_thread);
|
|
|
|
LEAVE();
|
|
return MLAN_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief This function triggers re-association by waking up
|
|
* re-assoc thread.
|
|
*
|
|
* @param context A pointer to context
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_reassoc_timer_func(void *context)
|
|
{
|
|
moal_handle *handle = (moal_handle *)context;
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MINFO, "reassoc_timer fired.\n");
|
|
handle->is_reassoc_timer_set = MFALSE;
|
|
|
|
PRINTM(MINFO, "Waking Up the Reassoc Thread\n");
|
|
wake_up_interruptible(&handle->reassoc_thread.wait_q);
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
#endif /* REASSOCIATION */
|
|
|
|
#ifdef STA_SUPPORT
|
|
/**
|
|
* @brief update dscp mapping from assoc_resp/reassoc_resp
|
|
*
|
|
* @param priv Pointer to the moal_private driver data struct
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_update_dscp_mapping(moal_private *priv)
|
|
{
|
|
mlan_ds_misc_assoc_rsp assoc_rsp;
|
|
IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
|
|
IEEEtypes_Header_t *qos_mapping_ie = NULL;
|
|
DSCP_Range_t *pdscp_range = NULL;
|
|
t_u8 dscp_except_num = 0;
|
|
DSCP_Exception_t dscp_except[MAX_DSCP_EXCEPTION_NUM];
|
|
int i, j;
|
|
ENTER();
|
|
|
|
memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp));
|
|
woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_NO_WAIT);
|
|
passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp.assoc_resp_buf;
|
|
memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map));
|
|
qos_mapping_ie =
|
|
(IEEEtypes_Header_t *)woal_parse_ie_tlv(passoc_rsp->ie_buffer,
|
|
assoc_rsp.
|
|
assoc_resp_len -
|
|
ASSOC_RESP_FIXED_SIZE,
|
|
QOS_MAPPING);
|
|
if (qos_mapping_ie &&
|
|
(qos_mapping_ie->len >= (sizeof(DSCP_Range_t) * MAX_NUM_TID))) {
|
|
dscp_except_num =
|
|
(qos_mapping_ie->len -
|
|
sizeof(DSCP_Range_t) * MAX_NUM_TID) /
|
|
sizeof(DSCP_Exception_t);
|
|
if (dscp_except_num > MAX_DSCP_EXCEPTION_NUM) {
|
|
PRINTM(MERROR, "dscp_except_num exceeds MAX limit\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
memcpy(dscp_except,
|
|
(t_u8 *)qos_mapping_ie + sizeof(IEEEtypes_Header_t),
|
|
dscp_except_num * sizeof(DSCP_Exception_t));
|
|
pdscp_range =
|
|
(DSCP_Range_t *)((t_u8 *)qos_mapping_ie +
|
|
sizeof(IEEEtypes_Header_t) +
|
|
dscp_except_num *
|
|
sizeof(DSCP_Exception_t));
|
|
for (i = 0; i < MAX_NUM_TID; i++) {
|
|
PRINTM(MEVENT, "TID %d: dscp_low=%d, dscp_high=%d\n", i,
|
|
pdscp_range->dscp_low_value,
|
|
pdscp_range->dscp_high_value);
|
|
if (pdscp_range->dscp_low_value != 0xff &&
|
|
pdscp_range->dscp_high_value != 0xff &&
|
|
pdscp_range->dscp_high_value <= 63) {
|
|
for (j = pdscp_range->dscp_low_value;
|
|
j <= pdscp_range->dscp_high_value; j++)
|
|
priv->dscp_map[j] = i;
|
|
}
|
|
pdscp_range++;
|
|
}
|
|
for (i = 0; i < dscp_except_num; i++) {
|
|
if ((dscp_except[i].dscp_value <= 63) &&
|
|
(dscp_except[i].user_priority <= 7)) {
|
|
PRINTM(MEVENT,
|
|
"dscp excpt: value=%d priority=%d\n",
|
|
dscp_except[i].dscp_value,
|
|
dscp_except[i].user_priority);
|
|
priv->dscp_map[dscp_except[i].dscp_value] =
|
|
dscp_except[i].user_priority;
|
|
}
|
|
}
|
|
}
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Sends disconnect event
|
|
*
|
|
* @param priv A pointer to moal_private struct
|
|
* @param disconnect_reason disconnect reason code
|
|
* @return N/A
|
|
*/
|
|
t_void
|
|
woal_send_disconnect_to_system(moal_private *priv, t_u16 disconnect_reason)
|
|
{
|
|
int custom_len = 0;
|
|
t_u8 event_buf[32];
|
|
#ifdef STA_WEXT
|
|
union iwreq_data wrqu;
|
|
#endif
|
|
#ifdef STA_CFG80211
|
|
unsigned long flags;
|
|
#endif
|
|
mlan_ds_misc_gtk_rekey_data zero_gtk;
|
|
t_u16 reason_code = 0;
|
|
|
|
ENTER();
|
|
priv->media_connected = MFALSE;
|
|
if (!disconnect_reason)
|
|
reason_code = MLAN_REASON_DEAUTH_LEAVING;
|
|
else
|
|
reason_code = disconnect_reason;
|
|
woal_stop_queue(priv->netdev);
|
|
if (netif_carrier_ok(priv->netdev))
|
|
netif_carrier_off(priv->netdev);
|
|
woal_flush_tcp_sess_queue(priv);
|
|
|
|
priv->gtk_data_ready = MFALSE;
|
|
memset(&zero_gtk, 0x00, sizeof(zero_gtk));
|
|
if (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);
|
|
}
|
|
memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data));
|
|
|
|
if (priv->bss_type == MLAN_BSS_TYPE_STA)
|
|
woal_flush_tdls_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 && priv->hist_data)
|
|
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);
|
|
/* 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);
|
|
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;
|
|
memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map));
|
|
}
|
|
#endif /* STA_CFG80211 */
|
|
|
|
memset(event_buf, 0, sizeof(event_buf));
|
|
custom_len = strlen(CUS_EVT_AP_CONNECTED);
|
|
strncpy(event_buf, CUS_EVT_AP_CONNECTED,
|
|
MIN((sizeof(event_buf) - 1), custom_len));
|
|
woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN);
|
|
LEAVE();
|
|
}
|
|
#endif /* STA_SUPPORT */
|
|
|
|
#define OFFSET_SEQNUM 4
|
|
/**
|
|
* @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)
|
|
{
|
|
struct file *pfile_fwdump = NULL;
|
|
loff_t pos = 0;
|
|
t_u16 seqnum;
|
|
t_u8 path_name[64];
|
|
|
|
ENTER();
|
|
if (phandle->fwdump_fname)
|
|
pfile_fwdump =
|
|
filp_open(phandle->fwdump_fname,
|
|
O_CREAT | O_WRONLY | O_APPEND, 0644);
|
|
else {
|
|
seqnum = woal_le16_to_cpu(*(t_u16 *)
|
|
(pmevent->event_buf + OFFSET_SEQNUM));
|
|
if (seqnum == 1) {
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
|
|
/** Create dump directort*/
|
|
woal_create_dump_dir(phandle, path_name,
|
|
sizeof(path_name));
|
|
#else
|
|
memset(path_name, 0, sizeof(path_name));
|
|
strcpy(path_name, "/data");
|
|
#endif
|
|
PRINTM(MMSG, "Firmware Dump directory name is %s\n",
|
|
path_name);
|
|
woal_dump_drv_info(phandle, path_name);
|
|
if (fwdump_fname) {
|
|
memset(fwdump_fname, 0, 64);
|
|
} else {
|
|
gfp_t flag;
|
|
flag = (in_atomic() ||
|
|
irqs_disabled())? GFP_ATOMIC :
|
|
GFP_KERNEL;
|
|
fwdump_fname = kzalloc(64, flag);
|
|
}
|
|
sprintf(fwdump_fname, "%s/file_fwdump", path_name);
|
|
pfile_fwdump =
|
|
filp_open(fwdump_fname,
|
|
O_CREAT | O_WRONLY | O_APPEND, 0644);
|
|
if (IS_ERR(pfile_fwdump)) {
|
|
memset(fwdump_fname, 0, 64);
|
|
sprintf(fwdump_fname, "%s/%s", "/var",
|
|
"file_fwdump");
|
|
pfile_fwdump =
|
|
filp_open(fwdump_fname,
|
|
O_CREAT | O_WRONLY | O_APPEND,
|
|
0644);
|
|
}
|
|
} else
|
|
pfile_fwdump =
|
|
filp_open(fwdump_fname,
|
|
O_CREAT | O_WRONLY | O_APPEND, 0644);
|
|
}
|
|
if (IS_ERR(pfile_fwdump)) {
|
|
PRINTM(MERROR, "Cannot create firmware dump file\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
vfs_write(pfile_fwdump, pmevent->event_buf, pmevent->event_len, &pos);
|
|
#else
|
|
kernel_write(pfile_fwdump, pmevent->event_buf, pmevent->event_len,
|
|
&pos);
|
|
#endif
|
|
filp_close(pfile_fwdump, NULL);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
#define DRV_INFO_SIZE 0x60000
|
|
#define ROW_SIZE_16 16
|
|
#define ROW_SIZE_32 32
|
|
/**
|
|
* @brief This function save moal_priv's debug log
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param buf A pointer buffer saving log
|
|
*
|
|
* @return The length of this log
|
|
*/
|
|
static int
|
|
woal_dump_priv_drv_info(moal_handle *handle, t_u8 *buf)
|
|
{
|
|
char *ptr = (char *)buf;
|
|
int index;
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
int i = 0;
|
|
#endif
|
|
moal_private *priv;
|
|
|
|
ENTER();
|
|
if (!handle || !buf) {
|
|
PRINTM(MMSG, "%s: can't retreive info\n", __func__);
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
for (index = 0; index < MIN(handle->priv_num, MLAN_MAX_BSS_NUM);
|
|
index++) {
|
|
priv = handle->priv[index];
|
|
if (priv) {
|
|
ptr += sprintf(ptr, "[Interface : %s]\n",
|
|
priv->proc_entry_name);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
ptr += sprintf(ptr, "wmm_tx_pending[0] = %d\n",
|
|
atomic_read(&priv->wmm_tx_pending[0]));
|
|
ptr += sprintf(ptr, "wmm_tx_pending[1] = %d\n",
|
|
atomic_read(&priv->wmm_tx_pending[1]));
|
|
ptr += sprintf(ptr, "wmm_tx_pending[2] = %d\n",
|
|
atomic_read(&priv->wmm_tx_pending[2]));
|
|
ptr += sprintf(ptr, "wmm_tx_pending[3] = %d\n",
|
|
atomic_read(&priv->wmm_tx_pending[3]));
|
|
#endif
|
|
ptr += sprintf(ptr, "Media state = \"%s\"\n",
|
|
((priv->media_connected ==
|
|
MFALSE) ? "Disconnected" :
|
|
"Connected"));
|
|
ptr += sprintf(ptr, "carrier %s\n",
|
|
((netif_carrier_ok(priv->netdev)) ? "on"
|
|
: "off"));
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
for (i = 0; i < (priv->netdev->num_tx_queues); i++) {
|
|
ptr += sprintf(ptr, "tx queue %d: %s\n", i,
|
|
((netif_tx_queue_stopped
|
|
(netdev_get_tx_queue
|
|
(priv->netdev,
|
|
i))) ? "stopped" :
|
|
"started"));
|
|
}
|
|
#else
|
|
ptr += sprintf(ptr, "tx queue %s\n",
|
|
((netif_queue_stopped(priv->netdev)) ?
|
|
"stopped" : "started"));
|
|
#endif
|
|
ptr += sprintf(ptr, "%s: num_tx_timeout = %d\n",
|
|
priv->netdev->name,
|
|
priv->num_tx_timeout);
|
|
}
|
|
}
|
|
|
|
LEAVE();
|
|
return ptr - (char *)buf;
|
|
}
|
|
|
|
/**
|
|
* @brief This function save moal_handle's info
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param buf A pointer buffer saving log
|
|
*
|
|
* @return The length of this log
|
|
*/
|
|
static int
|
|
woal_dump_moal_drv_info(moal_handle *phandle, t_u8 *buf)
|
|
{
|
|
char *ptr;
|
|
char str_buf[MLAN_MAX_VER_STR_LEN];
|
|
|
|
ENTER();
|
|
if (!phandle || !buf) {
|
|
PRINTM(MMSG, "%s: can't retreive info\n", __func__);
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
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);
|
|
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));
|
|
ptr += sprintf(ptr, "malloc_cons_count = %d\n",
|
|
atomic_read(&phandle->malloc_cons_count));
|
|
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");
|
|
|
|
ptr += woal_dump_pcie_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;
|
|
char str[11 * DBG_CMD_NUM + 1] = { 0 };
|
|
char *s;
|
|
|
|
ENTER();
|
|
if (!priv || 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, "num_cmd_timeout = %d\n", info.num_cmd_timeout);
|
|
ptr += sprintf(ptr, "dbg.num_cmd_timeout = %d\n",
|
|
info.dbg_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);
|
|
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, "cmd_resp_received = %d\n", info.cmd_resp_received);
|
|
ptr += sprintf(ptr, "event_received = %d\n", info.event_received);
|
|
ptr += sprintf(ptr, "max_tx_buf_size = %d\n", info.max_tx_buf_size);
|
|
ptr += sprintf(ptr, "tx_buf_size = %d\n", info.tx_buf_size);
|
|
ptr += sprintf(ptr, "curr_tx_buf_size = %d\n", info.curr_tx_buf_size);
|
|
|
|
ptr += sprintf(ptr, "data_sent=%d cmd_sent=%d\n", info.data_sent,
|
|
info.cmd_sent);
|
|
ptr += sprintf(ptr, "ps_mode=%d ps_state=%d\n", info.ps_mode,
|
|
info.ps_state);
|
|
ptr += sprintf(ptr, "wakeup_dev_req=%d wakeup_tries=%d\n",
|
|
info.pm_wakeup_card_req, info.pm_wakeup_fw_try);
|
|
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);
|
|
|
|
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, "------------mlan_debug_info End-------------\n");
|
|
|
|
LEAVE();
|
|
return ptr - (char *)buf;
|
|
}
|
|
|
|
/**
|
|
* @brief This function dump hex to file
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param buf A pointer to buffer to dump
|
|
* @param len lengh of buf
|
|
* @param ascii Whether add ascii at the end
|
|
* @param save_buf Buffer which is saved to
|
|
*
|
|
* @return The length of this log
|
|
*/
|
|
static int
|
|
woal_save_hex_dump(int rowsize, const void *buf, size_t len,
|
|
bool ascii, t_u8 *save_buf)
|
|
{
|
|
const u8 *ptr = buf;
|
|
int i, linelen, remaining = len;
|
|
unsigned char linebuf[ROW_SIZE_32 * 3 + 2 + ROW_SIZE_32 + 1];
|
|
char *pos = (char *)save_buf;
|
|
|
|
if (rowsize != ROW_SIZE_16 && rowsize != ROW_SIZE_32)
|
|
rowsize = ROW_SIZE_16;
|
|
|
|
for (i = 0; i < len; i += rowsize) {
|
|
linelen = min(remaining, rowsize);
|
|
remaining -= rowsize;
|
|
|
|
hex_dump_to_buffer(ptr + i, linelen, rowsize, 1, linebuf,
|
|
sizeof(linebuf), ascii);
|
|
|
|
pos += sprintf(pos, "%s\n", linebuf);
|
|
}
|
|
|
|
return pos - (char *)save_buf;
|
|
}
|
|
|
|
#define HostCmd_CMD_CFG_DATA 0x008f
|
|
#define DEF_FW_PATH "/lib/firmware/"
|
|
#define DEF_HOSTCMD_PATH "/lib/firmware/mrvl/hostcmd.conf"
|
|
/**
|
|
* @brief This function save the hostcmd response to file
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param pevent A pointer to mlan_cmdresp_event
|
|
*
|
|
* @return N/A
|
|
*/
|
|
t_void
|
|
woal_save_host_cmdresp(moal_handle *phandle, mlan_cmdresp_event * pevent)
|
|
{
|
|
HostCmd_DS_GEN *resp;
|
|
char file_path[256];
|
|
struct file *pfile = NULL;
|
|
int ret;
|
|
t_u8 *buf;
|
|
t_u16 command;
|
|
int len = 0;
|
|
char *ptr;
|
|
loff_t pos = 0;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
mm_segment_t fs;
|
|
#endif
|
|
|
|
resp = (HostCmd_DS_GEN *)pevent->resp;
|
|
command = woal_le16_to_cpu(resp->command);
|
|
memset(file_path, 0, sizeof(file_path));
|
|
ret = moal_vmalloc(phandle, pevent->event_len * 5, &buf);
|
|
if (ret != MLAN_STATUS_SUCCESS || !buf) {
|
|
PRINTM(MERROR, "Fail to allocate memory to save hostcmd\n");
|
|
return;
|
|
}
|
|
memset(buf, 0, pevent->event_len * 5);
|
|
ptr = (char *)buf;
|
|
switch (command) {
|
|
case HostCmd_CMD_CFG_DATA:
|
|
if (dpd_data_cfg)
|
|
sprintf(file_path, "%s%s", DEF_FW_PATH, dpd_data_cfg);
|
|
else
|
|
sprintf(file_path, "%s", DEF_HOSTCMD_PATH);
|
|
break;
|
|
default:
|
|
sprintf(file_path, "%s", DEF_HOSTCMD_PATH);
|
|
break;
|
|
}
|
|
pfile = filp_open(file_path, O_CREAT | O_WRONLY | O_APPEND, 0644);
|
|
if (IS_ERR(pfile)) {
|
|
PRINTM(MERROR, "Cannot create file %s\n", file_path);
|
|
moal_vfree(phandle, buf);
|
|
return;
|
|
}
|
|
ptr += sprintf(ptr, "hostcmd_%02x=={\n", command);
|
|
ptr += woal_save_hex_dump(ROW_SIZE_16, resp, pevent->event_len, MFALSE,
|
|
ptr);
|
|
ptr += sprintf(ptr, "}\n");
|
|
len = ptr - (char *)buf;
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
vfs_write(pfile, buf, len, &pos);
|
|
set_fs(fs);
|
|
#else
|
|
kernel_write(pfile, buf, len, &pos);
|
|
#endif
|
|
PRINTM(MMSG, "Save hostcmd 0x%02x, cmd len=%d file len=%d to file %s\n",
|
|
command, pevent->event_len, len, file_path);
|
|
if (buf)
|
|
moal_vfree(phandle, buf);
|
|
filp_close(pfile, NULL);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function dump moal hex to file
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param buf A pointer to buffer
|
|
*
|
|
* @return The length of this log
|
|
*/
|
|
static int
|
|
woal_dump_moal_hex(moal_handle *phandle, t_u8 *buf)
|
|
{
|
|
char *ptr = (char *)buf;
|
|
int i;
|
|
ENTER();
|
|
|
|
if (!phandle || !buf) {
|
|
PRINTM(MMSG, "%s: can't retreive info\n", __func__);
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
ptr += sprintf(ptr, "<--moal_handle-->\n");
|
|
ptr += sprintf(ptr, "moal_handle=%p, size=%ld(0x%lx)\n", phandle,
|
|
(long int)sizeof(*phandle),
|
|
(long unsigned int)sizeof(*phandle));
|
|
ptr += woal_save_hex_dump(ROW_SIZE_16, phandle, sizeof(*phandle), MTRUE,
|
|
ptr);
|
|
ptr += sprintf(ptr, "<--moal_handle End-->\n");
|
|
|
|
for (i = 0; i < phandle->priv_num; i++) {
|
|
ptr += sprintf(ptr, "<--moal_private(%d)-->\n", i);
|
|
ptr += sprintf(ptr, "moal_private=%p, size=%ld(0x%lx)\n",
|
|
phandle->priv[i],
|
|
(long int)sizeof(*(phandle->priv[i])),
|
|
(long unsigned int)sizeof(*(phandle->priv[i])));
|
|
ptr += woal_save_hex_dump(ROW_SIZE_16, phandle->priv[i],
|
|
sizeof(*(phandle->priv[i])), MTRUE,
|
|
ptr);
|
|
ptr += sprintf(ptr, "<--moal_private(%d) End-->\n", i);
|
|
}
|
|
LEAVE();
|
|
return ptr - (char *)buf;
|
|
}
|
|
|
|
/**
|
|
* @brief This function dump mlan hex to file
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
* @param buf A pointer to buffer
|
|
*
|
|
* @return The length of this log
|
|
*/
|
|
static int
|
|
woal_dump_mlan_hex(moal_private *priv, t_u8 *buf)
|
|
{
|
|
char *ptr = (char *)buf;
|
|
int i;
|
|
ENTER();
|
|
|
|
if (!buf || !priv || 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");
|
|
for (i = 0; i < info.mlan_priv_num; i++) {
|
|
ptr += sprintf(ptr, "<--mlan_private(%d)-->\n", i);
|
|
ptr += sprintf(ptr, "mlan_private=%p, size=%d(0x%x)\n",
|
|
info.mlan_priv[i], info.mlan_priv_size[i],
|
|
info.mlan_priv_size[i]);
|
|
ptr += woal_save_hex_dump(ROW_SIZE_16, info.mlan_priv[i],
|
|
info.mlan_priv_size[i], MTRUE, ptr);
|
|
ptr += sprintf(ptr, "<--mlan_private(%d) End-->\n", i);
|
|
}
|
|
|
|
LEAVE();
|
|
return ptr - (char *)buf;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
|
|
/**
|
|
* @brief This function create dump directory
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param dir_buf A pointer to dir_buf buffer
|
|
* @param buf_size Size of dir_buf buffer
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_create_dump_dir(moal_handle *phandle, char *dir_buf, int buf_size)
|
|
{
|
|
struct dentry *dentry;
|
|
struct path path;
|
|
t_u32 sec, usec;
|
|
int ret;
|
|
|
|
ENTER();
|
|
|
|
if (!phandle || !dir_buf) {
|
|
PRINTM(MERROR, "Can't create directory\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
moal_get_system_time(phandle, &sec, &usec);
|
|
memset(dir_buf, 0, buf_size);
|
|
sprintf(dir_buf, "%s%u", "/data/dump_", sec);
|
|
|
|
dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1);
|
|
if (IS_ERR(dentry)) {
|
|
PRINTM(MERROR,
|
|
"Create directory %s error, try create dir in /var",
|
|
dir_buf);
|
|
memset(dir_buf, 0, buf_size);
|
|
sprintf(dir_buf, "%s%u", "/var/dump_", sec);
|
|
dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1);
|
|
}
|
|
if (IS_ERR(dentry)) {
|
|
PRINTM(MERROR, "Create directory %s error, use default folder",
|
|
dir_buf);
|
|
goto default_dir;
|
|
}
|
|
ret = vfs_mkdir(path.dentry->d_inode, dentry, 0777);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
|
|
mutex_unlock(&path.dentry->d_inode->i_mutex);
|
|
#else
|
|
inode_unlock(path.dentry->d_inode);
|
|
#endif
|
|
|
|
if (ret < 0) {
|
|
PRINTM(MERROR,
|
|
"Create directory failure, use default folder\n");
|
|
PRINTM(MERROR, "Create directory failure, ret = %d\n", ret);
|
|
goto default_dir;
|
|
} else {
|
|
PRINTM(MERROR, "Create directory %s successfully\n", dir_buf);
|
|
goto done;
|
|
}
|
|
|
|
default_dir:
|
|
memset(dir_buf, 0, buf_size);
|
|
sprintf(dir_buf, "%s", "/data");
|
|
done:
|
|
LEAVE();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function save dump buf to file
|
|
*
|
|
* @param dir_name A pointer to directory name
|
|
* @param file_name A pointer to file name
|
|
* @param buf A pointer to dump data
|
|
* @param buf_len The length of dump buf
|
|
*
|
|
* @return SUCCESS OR FAILURE
|
|
*/
|
|
mlan_status
|
|
woal_save_dump_info_to_file(char *dir_name, char *file_name, t_u8 *buf,
|
|
t_u32 buf_len)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
struct file *pfile = NULL;
|
|
t_u8 name[64];
|
|
loff_t pos;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
mm_segment_t fs;
|
|
#endif
|
|
|
|
ENTER();
|
|
|
|
if (!dir_name || !file_name || !buf) {
|
|
PRINTM(MERROR, "Can't save dump info to file\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
memset(name, 0, sizeof(name));
|
|
sprintf(name, "%s/%s", dir_name, file_name);
|
|
pfile = filp_open(name, O_CREAT | O_RDWR, 0644);
|
|
if (IS_ERR(pfile)) {
|
|
PRINTM(MERROR,
|
|
"Create file %s error, try to save dump file in /var\n",
|
|
name);
|
|
memset(name, 0, sizeof(name));
|
|
sprintf(name, "%s/%s", "/var", file_name);
|
|
pfile = filp_open(name, O_CREAT | O_RDWR, 0644);
|
|
}
|
|
if (IS_ERR(pfile)) {
|
|
PRINTM(MERROR, "Create Dump file for %s error\n", name);
|
|
ret = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
PRINTM(MERROR, "Dump data %s saved in %s\n", file_name, name);
|
|
|
|
pos = 0;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
vfs_write(pfile, buf, buf_len, &pos);
|
|
set_fs(fs);
|
|
#else
|
|
kernel_write(pfile, buf, buf_len, &pos);
|
|
#endif
|
|
filp_close(pfile, NULL);
|
|
|
|
PRINTM(MERROR, "Dump data %s saved in %s successfully\n", file_name,
|
|
name);
|
|
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function dump drv info to file
|
|
*
|
|
* @param phandle A pointer to moal_handle
|
|
* @param dir_name A pointer to directory name
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_dump_drv_info(moal_handle *phandle, t_u8 *dir_name)
|
|
{
|
|
int ret = 0;
|
|
struct file *pfile = NULL;
|
|
t_u8 *drv_buf;
|
|
t_u8 file_name[64];
|
|
t_u32 len = 0;
|
|
t_u32 total_len = 0;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
mm_segment_t fs;
|
|
#endif
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MERROR, "=== START DRIVER INFO DUMP===");
|
|
ret = moal_vmalloc(phandle, DRV_INFO_SIZE + 1, &drv_buf);
|
|
if ((ret != MLAN_STATUS_SUCCESS) || !drv_buf) {
|
|
PRINTM(MERROR, "Error: vmalloc drv buffer failed!\n");
|
|
goto done;
|
|
}
|
|
memset(file_name, 0, sizeof(file_name));
|
|
sprintf(file_name, "%s/%s", dir_name, "file_drv_info");
|
|
pfile = filp_open(file_name, O_CREAT | O_RDWR, 0644);
|
|
if (IS_ERR(pfile)) {
|
|
PRINTM(MERROR,
|
|
"Create file %s error, try create /var/file_drv_info",
|
|
file_name);
|
|
pfile = filp_open("/var/file_drv_info", O_CREAT | O_RDWR, 0644);
|
|
} else {
|
|
PRINTM(MERROR, "DRV dump data in %s\n", file_name);
|
|
}
|
|
if (IS_ERR(pfile)) {
|
|
PRINTM(MERROR, "Create file_drv_info file failed\n");
|
|
goto done;
|
|
}
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
#endif
|
|
|
|
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;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
vfs_write(pfile, drv_buf, total_len, &pfile->f_pos);
|
|
#else
|
|
kernel_write(pfile, drv_buf, total_len, &pfile->f_pos);
|
|
#endif
|
|
PRINTM(MERROR, "Drv info total bytes = %ld (0x%lx)\n",
|
|
(long int)total_len, (long unsigned int)total_len);
|
|
|
|
filp_close(pfile, NULL);
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
|
set_fs(fs);
|
|
#endif
|
|
PRINTM(MERROR, "=== DRIVER INFO DUMP END===");
|
|
done:
|
|
if (drv_buf)
|
|
moal_vfree(phandle, drv_buf);
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function displays extra MOAL debug information
|
|
*
|
|
* @param priv A pointer to moal_private
|
|
* @param handle A pointer to moal_handle
|
|
* @param flag Indicates whether register read can be done directly
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
woal_moal_debug_info(moal_private *priv, moal_handle *handle, u8 flag)
|
|
{
|
|
moal_handle *phandle = NULL;
|
|
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;
|
|
}
|
|
|
|
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));
|
|
PRINTM(MERROR, "malloc_cons_count = %d\n",
|
|
atomic_read(&phandle->malloc_cons_count));
|
|
PRINTM(MERROR, "hs_skip_count = %u\n", phandle->hs_skip_count);
|
|
PRINTM(MERROR, "hs_force_count = %u\n", phandle->hs_force_count);
|
|
|
|
if (priv) {
|
|
PRINTM(MERROR, "Media state = \"%s\"\n",
|
|
((priv->media_connected ==
|
|
MFALSE) ? "Disconnected" : "Connected"));
|
|
PRINTM(MERROR, "carrier %s\n",
|
|
((netif_carrier_ok(priv->netdev)) ? "on" : "off"));
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
for (i = 0; i < (priv->netdev->num_tx_queues); i++) {
|
|
PRINTM(MERROR, "tx queue %d: %s\n", i,
|
|
((netif_tx_queue_stopped
|
|
(netdev_get_tx_queue(priv->netdev, i))) ?
|
|
"stopped" : "started"));
|
|
}
|
|
#else
|
|
PRINTM(MERROR, "tx queue %s\n",
|
|
((netif_queue_stopped(priv->netdev)) ? "stopped" :
|
|
"started"));
|
|
#endif
|
|
}
|
|
|
|
for (i = 0; i < phandle->priv_num; i++) {
|
|
priv = phandle->priv[i];
|
|
if (priv)
|
|
PRINTM(MERROR, "%s: num_tx_timeout = %d\n",
|
|
priv->netdev->name, priv->num_tx_timeout);
|
|
}
|
|
|
|
if (phandle->is_suspended == MTRUE) {
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
woal_pcie_reg_dbg(phandle);
|
|
if (drvdbg & MFW_D) {
|
|
drvdbg &= ~MFW_D;
|
|
phandle->fw_dbg = MTRUE;
|
|
queue_work(phandle->workqueue, &phandle->main_work);
|
|
}
|
|
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Download power table to firmware for a specific country
|
|
*
|
|
* @param priv A pointer to moal_private
|
|
* @param country ISO 3166-1 alpha-2 country code
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status
|
|
woal_request_country_power_table(moal_private *priv, char *country)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
moal_handle *handle = NULL;
|
|
char country_name[] = "txpower_XX.bin";
|
|
char file_path[256];
|
|
char *last_slash = NULL;
|
|
|
|
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;
|
|
|
|
/* Replace XX with ISO 3166-1 alpha-2 country code */
|
|
strncpy(strstr(country_name, "XX"), country, strlen(country));
|
|
|
|
memset(file_path, 0, sizeof(file_path));
|
|
/* file_path should be Null terminated */
|
|
if (fw_name && (strlen(fw_name) < sizeof(file_path))) {
|
|
strncpy(file_path, fw_name,
|
|
MIN((sizeof(file_path) - 1), strlen(fw_name)));
|
|
last_slash = strrchr(file_path, '/');
|
|
if (last_slash)
|
|
memset(last_slash + 1, 0,
|
|
sizeof(file_path) - 1 - (last_slash -
|
|
file_path));
|
|
else
|
|
memset(file_path, 0, sizeof(file_path));
|
|
} else {
|
|
strncpy(file_path, "mrvl/", sizeof(file_path));
|
|
}
|
|
strncpy(file_path + strlen(file_path), country_name,
|
|
strlen(country_name));
|
|
country_txpwrlimit = file_path;
|
|
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_user_init_data(handle, COUNTRY_POWER_TABLE,
|
|
MOAL_IOCTL_WAIT)) {
|
|
PRINTM(MFATAL, "Download power table to firmware failed\n");
|
|
ret = MLAN_STATUS_FAILURE;
|
|
}
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief napi polling call back function.
|
|
*
|
|
* @param napi A pointer to napi_struct
|
|
* @param budget the limit of packets driver should poll
|
|
*
|
|
* @return packets received
|
|
*/
|
|
int
|
|
woal_netdev_poll_rx(struct napi_struct *napi, int budget)
|
|
{
|
|
moal_handle *handle = container_of(napi, moal_handle, napi_rx);
|
|
t_u8 recv = budget;
|
|
|
|
ENTER();
|
|
if (handle->surprise_removed == MTRUE) {
|
|
napi_complete(napi);
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
mlan_rx_process(handle->pmlan_adapter, &recv);
|
|
if (recv < budget)
|
|
napi_complete(napi);
|
|
LEAVE();
|
|
return recv;
|
|
}
|
|
|
|
/**
|
|
* @brief This workqueue function handles 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
|
|
|
|
ENTER();
|
|
if (handle->surprise_removed == MTRUE) {
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
#ifdef STA_CFG80211
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 14, 6)
|
|
if (handle->rx_bgscan_stop) {
|
|
handle->rx_bgscan_stop = MFALSE;
|
|
priv = handle->bg_scan_priv;
|
|
handle->bg_scan_priv = NULL;
|
|
if (priv && priv->sched_scanning) {
|
|
cfg80211_sched_scan_stopped(priv->wdev->wiphy);
|
|
PRINTM(MEVENT, "Report sched_Scan stopped\n");
|
|
priv->sched_scanning = MFALSE;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
mlan_rx_process(handle->pmlan_adapter, NULL);
|
|
|
|
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);
|
|
#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->fw_dbg == MTRUE) {
|
|
handle->fw_dbg = MFALSE;
|
|
woal_dump_firmware_info(handle);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
|
/* Change the priority and scheduling policy of main work queue */
|
|
if ((wq_sched_prio != current->rt_priority) ||
|
|
(wq_sched_policy != current->policy)) {
|
|
PRINTM(MMSG,
|
|
"Set work queue priority %d and scheduling policy %d\n",
|
|
wq_sched_prio, wq_sched_policy);
|
|
sched_setscheduler(current, wq_sched_policy, &sp);
|
|
}
|
|
#endif
|
|
|
|
handle->main_state = MOAL_ENTER_WORK_QUEUE;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#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;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
|
#endif
|
|
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief Handles interrupt
|
|
*
|
|
*/
|
|
/**
|
|
* @param msg_id A message id
|
|
* @param handle A pointer to moal_handle struct
|
|
*
|
|
* @return MLAN_STATUS_FAILURE-- when the interrupt is not for us.
|
|
*/
|
|
mlan_status
|
|
woal_interrupt(unsigned short msg_id, moal_handle *handle)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
ENTER();
|
|
handle->main_state = MOAL_RECV_INT;
|
|
PRINTM(MINTR, "*\n");
|
|
if (handle->surprise_removed == MTRUE) {
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
ret = mlan_interrupt(msg_id, handle->pmlan_adapter);
|
|
queue_work(handle->workqueue, &handle->main_work);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @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
|
|
*
|
|
* @return A pointer to moal_handle structure
|
|
*/
|
|
moal_handle *
|
|
woal_add_card(void *card)
|
|
{
|
|
moal_handle *handle = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
int netlink_num = NETLINK_MARVELL;
|
|
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;
|
|
|
|
/* 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 */
|
|
for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
|
|
if (m_handle[index] == NULL)
|
|
break;
|
|
}
|
|
if (index < MAX_MLAN_ADAPTER) {
|
|
m_handle[index] = handle;
|
|
handle->handle_idx = index;
|
|
} else {
|
|
PRINTM(MERROR, "Exceeded maximum cards supported!\n");
|
|
goto err_kmalloc;
|
|
}
|
|
|
|
if (mac_addr
|
|
#ifdef MFG_CMD_SUPPORT
|
|
&& mfg_mode != MLAN_INIT_PARA_ENABLED
|
|
#endif
|
|
) {
|
|
t_u8 temp[20];
|
|
t_u8 len = strlen(mac_addr) + 1;
|
|
if (len < sizeof(temp)) {
|
|
memcpy(temp, mac_addr, len);
|
|
handle->set_mac_addr = 1;
|
|
/* note: the following function overwrites the temp buffer */
|
|
woal_mac2u8(handle->mac_addr, temp);
|
|
}
|
|
}
|
|
|
|
handle->histogram_table_num = 1;
|
|
|
|
#ifdef SPI_SUPPORT
|
|
((struct woal_spi_card *)card)->handle = handle;
|
|
#endif
|
|
((pcie_service_card *)card)->handle = handle;
|
|
|
|
#ifdef STA_SUPPORT
|
|
handle->scan_pending_on_block = MFALSE;
|
|
MOAL_INIT_SEMAPHORE(&handle->async_sem);
|
|
#endif
|
|
|
|
/* 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);
|
|
|
|
if (!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 (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);
|
|
}
|
|
|
|
atomic_set(&handle->int_flag, 0);
|
|
|
|
#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 (woal_register_dev(handle) != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MFATAL, "Failed to register wlan device!\n");
|
|
goto err_registerdev;
|
|
}
|
|
#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;
|
|
}
|
|
|
|
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");
|
|
woal_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 (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;
|
|
((pcie_service_card *)card)->handle = 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;
|
|
#ifdef MFG_CMD_SUPPORT
|
|
if (mfg_mode == MLAN_INIT_PARA_ENABLED) {
|
|
fw_name = NULL;
|
|
mfg_mode = 0;
|
|
}
|
|
#endif
|
|
/* 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;
|
|
handle->surprise_removed = MTRUE;
|
|
|
|
flush_workqueue(handle->workqueue);
|
|
if (handle->rx_workqueue)
|
|
flush_workqueue(handle->rx_workqueue);
|
|
|
|
if (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) {
|
|
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");
|
|
}
|
|
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 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
|
|
#if defined(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
|
|
/* Remove interface */
|
|
for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++)
|
|
woal_remove_interface(handle, i);
|
|
|
|
woal_terminate_workqueue(handle);
|
|
|
|
#ifdef UAP_CFG80211
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
if (handle->is_cac_timer_set) {
|
|
woal_cancel_timer(&handle->cac_timer);
|
|
handle->is_cac_timer_set = MFALSE;
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef REASSOCIATION
|
|
PRINTM(MINFO, "Free reassoc_timer\n");
|
|
if (handle->is_reassoc_timer_set) {
|
|
woal_cancel_timer(&handle->reassoc_timer);
|
|
handle->is_reassoc_timer_set = MFALSE;
|
|
}
|
|
if (handle->reassoc_thread.pid)
|
|
wake_up_interruptible(&handle->reassoc_thread.wait_q);
|
|
|
|
/* waiting for main thread quit */
|
|
while (handle->reassoc_thread.pid)
|
|
woal_sched_timeout(2);
|
|
#endif /* REASSOCIATION */
|
|
#ifdef CONFIG_PROC_FS
|
|
woal_proc_exit(handle);
|
|
#endif
|
|
/* Unregister device */
|
|
PRINTM(MINFO, "unregister device\n");
|
|
woal_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;
|
|
}
|
|
|
|
/* 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");
|
|
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);
|
|
|
|
/* Remove interface */
|
|
for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++)
|
|
woal_remove_interface(handle, i);
|
|
|
|
/* Unregister mlan */
|
|
if (handle->pmlan_adapter) {
|
|
mlan_unregister(handle->pmlan_adapter);
|
|
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));
|
|
}
|
|
if (atomic_read(&handle->malloc_cons_count)) {
|
|
PRINTM(MERROR,
|
|
"mlan has memory leak: malloc_cons_count=%d\n",
|
|
atomic_read(&handle->malloc_cons_count));
|
|
}
|
|
handle->pmlan_adapter = NULL;
|
|
}
|
|
|
|
handle->priv_num = 0;
|
|
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
|
|
|
|
/**
|
|
* @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 reload fw
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param mode FW reload mode
|
|
*
|
|
* @return 0--success, otherwise failure
|
|
*/
|
|
void
|
|
woal_request_fw_reload(moal_handle *handle, t_u8 mode)
|
|
{
|
|
int intf_num;
|
|
int ret = 0;
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_misc_cfg *misc = NULL;
|
|
#if defined(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 && V14_FEATURE */
|
|
pcie_service_card *card = NULL;
|
|
struct pci_dev *pdev = NULL;
|
|
|
|
ENTER();
|
|
if (mode == FW_RELOAD_PCIE_RESET) {
|
|
card = (pcie_service_card *)handle->card;
|
|
pdev = card->dev;
|
|
pci_reset_function(pdev);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/** start block IOCTL */
|
|
handle->driver_state = MTRUE;
|
|
if (mode == FW_RELOAD_WITH_EMULATION) {
|
|
fw_reload = FW_RELOAD_WITH_EMULATION;
|
|
PRINTM(MMSG, "FW reload with re-emulation...\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/** detach network interface */
|
|
for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
|
|
woal_stop_queue(handle->priv[intf_num]->netdev);
|
|
netif_device_detach(handle->priv[intf_num]->netdev);
|
|
}
|
|
handle->fw_reload = mode;
|
|
woal_update_firmware_name(handle);
|
|
if (mode == FW_RELOAD_NO_EMULATION) {
|
|
ret = woal_reload_fw(handle);
|
|
} else
|
|
ret = -EFAULT;
|
|
if (ret) {
|
|
PRINTM(MERROR, "FW reload fail\n");
|
|
goto done;
|
|
}
|
|
/** un-block IOCTL */
|
|
handle->fw_reload = 0;
|
|
handle->driver_state = 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)) {
|
|
ret = -EFAULT;
|
|
kfree(req);
|
|
goto done;
|
|
}
|
|
kfree(req);
|
|
}
|
|
handle->hardware_status = HardwareStatusReady;
|
|
/* Reset all interfaces */
|
|
ret = woal_reset_intf(woal_get_priv(handle, MLAN_BSS_ROLE_ANY),
|
|
MOAL_IOCTL_WAIT, MTRUE);
|
|
/* Initialize private structures */
|
|
for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
|
|
woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT);
|
|
#if defined(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)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
#endif /* STA_WEXT || UAP_WEXT */
|
|
#endif /* STA_SUPPORT && UAP_SUPPORT */
|
|
#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */
|
|
}
|
|
|
|
/* Enable interfaces */
|
|
for (intf_num = 0; intf_num < handle->priv_num; intf_num++) {
|
|
netif_device_attach(handle->priv[intf_num]->netdev);
|
|
woal_start_queue(handle->priv[intf_num]->netdev);
|
|
}
|
|
done:
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief This function 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);
|
|
|
|
#ifdef CONFIG_OF
|
|
woal_init_from_dev_tree();
|
|
#endif
|
|
|
|
/* Create workqueue for hang process */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
|
|
/* For kernel less than 2.6.14 name can not be greater than 10
|
|
characters */
|
|
hang_workqueue = create_workqueue("MOAL_HANG_WORKQ");
|
|
#else
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
|
|
hang_workqueue = alloc_workqueue("MOAL_HANG_WORK_QUEUE",
|
|
WQ_HIGHPRI | WQ_MEM_RECLAIM |
|
|
WQ_UNBOUND, 1);
|
|
#else
|
|
hang_workqueue = create_workqueue("MOAL_HANG_WORK_QUEUE");
|
|
#endif
|
|
#endif
|
|
MLAN_INIT_WORK(&hang_work, woal_hang_work_queue);
|
|
|
|
/* Register with bus */
|
|
ret = woal_bus_register();
|
|
if (ret == MLAN_STATUS_SUCCESS)
|
|
PRINTM(MMSG, "wlan: Driver loaded successfully\n");
|
|
else
|
|
PRINTM(MMSG, "wlan: Driver loading failed\n");
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief This function cleans module
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void
|
|
woal_cleanup_module(void)
|
|
{
|
|
moal_handle *handle = NULL;
|
|
int index = 0;
|
|
int i;
|
|
#if defined(STA_SUPPORT) && defined(STA_CFG80211)
|
|
unsigned long flags;
|
|
#endif
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MMSG, "wlan: Unloading MWLAN driver\n");
|
|
if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem))
|
|
goto exit_sem_err;
|
|
for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
|
|
handle = m_handle[index];
|
|
if (!handle)
|
|
continue;
|
|
if (!handle->priv_num)
|
|
goto exit;
|
|
if (MTRUE == woal_check_driver_status(handle))
|
|
goto exit;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
#ifdef STA_SUPPORT
|
|
if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) {
|
|
if (handle->priv[i]->media_connected == MTRUE) {
|
|
woal_disconnect(handle->priv[i],
|
|
MOAL_IOCTL_WAIT_TIMEOUT,
|
|
NULL,
|
|
DEF_DEAUTH_REASON_CODE);
|
|
if (handle->ioctl_timeout) {
|
|
woal_ioctl_timeout(handle);
|
|
goto exit;
|
|
}
|
|
}
|
|
#ifdef STA_CFG80211
|
|
if (IS_STA_CFG80211(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(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(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 (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 (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 (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;
|
|
}
|
|
|
|
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(hw_test, int, 0660);
|
|
MODULE_PARM_DESC(hw_test, "0: Disable hardware test; 1: Enable hardware test");
|
|
#ifdef CONFIG_OF
|
|
module_param(dts_enable, int, 0);
|
|
MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS");
|
|
#endif
|
|
module_param(fw_name, charp, 0660);
|
|
MODULE_PARM_DESC(fw_name, "Firmware name");
|
|
module_param(req_fw_nowait, int, 0);
|
|
MODULE_PARM_DESC(req_fw_nowait,
|
|
"0: Use request_firmware API; 1: Use request_firmware_nowait API");
|
|
module_param(fw_reload, int, 0);
|
|
MODULE_PARM_DESC(fw_reload,
|
|
"0: disable fw_reload; 1: enable fw reload feature");
|
|
module_param(fw_serial, int, 0);
|
|
MODULE_PARM_DESC(fw_serial,
|
|
"0: support parallel download FW; 1: support serial download FW");
|
|
module_param(fw_region, int, 0);
|
|
MODULE_PARM_DESC(fw_region, "1: create channel regulatory domain from FW");
|
|
module_param(mac_addr, charp, 0660);
|
|
MODULE_PARM_DESC(mac_addr, "MAC address");
|
|
#ifdef MFG_CMD_SUPPORT
|
|
module_param(mfg_mode, int, 0660);
|
|
MODULE_PARM_DESC(mfg_mode,
|
|
"0: Download normal firmware; 1/2: Download MFG firmware");
|
|
#endif /* MFG_CMD_SUPPORT */
|
|
module_param(drv_mode, int, 0660);
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
MODULE_PARM_DESC(drv_mode,
|
|
"Bit 0: STA; Bit 1: uAP; Bit 2: WIFIDIRECT; Bit 4: NAN");
|
|
#else
|
|
MODULE_PARM_DESC(drv_mode, "Bit 0: STA; Bit 1: uAP; Bit 4: NAN");
|
|
#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */
|
|
#ifdef STA_SUPPORT
|
|
module_param(max_sta_bss, int, 0);
|
|
MODULE_PARM_DESC(max_sta_bss, "Number of STA interfaces (1)");
|
|
module_param(sta_name, charp, 0);
|
|
MODULE_PARM_DESC(sta_name, "STA interface name");
|
|
#endif /* STA_SUPPORT */
|
|
#ifdef UAP_SUPPORT
|
|
module_param(max_uap_bss, int, 0);
|
|
MODULE_PARM_DESC(max_uap_bss, "Number of uAP interfaces (1)");
|
|
module_param(uap_name, charp, 0);
|
|
MODULE_PARM_DESC(uap_name, "uAP interface name");
|
|
#endif /* UAP_SUPPORT */
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
module_param(max_wfd_bss, int, 0);
|
|
MODULE_PARM_DESC(max_wfd_bss, "Number of WIFIDIRECT interfaces (1)");
|
|
module_param(wfd_name, charp, 0);
|
|
MODULE_PARM_DESC(wfd_name, "WIFIDIRECT interface name");
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
module_param(max_vir_bss, int, 0);
|
|
MODULE_PARM_DESC(max_vir_bss, "Number of Virtual interfaces (0)");
|
|
#endif
|
|
#endif /* WIFI_DIRECT_SUPPORT && V14_FEATURE */
|
|
module_param(nan_name, charp, 0);
|
|
MODULE_PARM_DESC(nan_name, "NAN interface name");
|
|
module_param(max_nan_bss, int, 0);
|
|
MODULE_PARM_DESC(max_nan_bss, "Number of NAN interfaces (1)");
|
|
#ifdef DEBUG_LEVEL1
|
|
module_param(drvdbg, uint, 0660);
|
|
MODULE_PARM_DESC(drvdbg, "Driver debug");
|
|
#endif /* DEBUG_LEVEL1 */
|
|
module_param(auto_ds, int, 0660);
|
|
MODULE_PARM_DESC(auto_ds,
|
|
"0: MLAN default; 1: Enable auto deep sleep; 2: Disable auto deep sleep");
|
|
module_param(ps_mode, int, 0660);
|
|
MODULE_PARM_DESC(ps_mode,
|
|
"0: MLAN default; 1: Enable IEEE PS mode; 2: Disable IEEE PS mode");
|
|
module_param(max_tx_buf, int, 0);
|
|
MODULE_PARM_DESC(max_tx_buf, "Maximum Tx buffer size (2048/4096/8192)");
|
|
|
|
#if defined(STA_SUPPORT)
|
|
module_param(cfg_11d, int, 0);
|
|
MODULE_PARM_DESC(cfg_11d,
|
|
"0: MLAN default; 1: Enable 802.11d; 2: Disable 802.11d");
|
|
#endif
|
|
module_param(dpd_data_cfg, charp, 0);
|
|
MODULE_PARM_DESC(dpd_data_cfg, "DPD data file name");
|
|
module_param(init_cfg, charp, 0);
|
|
MODULE_PARM_DESC(init_cfg, "Init config file name");
|
|
module_param(cal_data_cfg, charp, 0);
|
|
MODULE_PARM_DESC(cal_data_cfg, "Calibration data file name");
|
|
module_param(txpwrlimit_cfg, charp, 0);
|
|
MODULE_PARM_DESC(txpwrlimit_cfg,
|
|
"Set configuration data of Tx power limitation");
|
|
module_param(cntry_txpwr, int, 0);
|
|
MODULE_PARM_DESC(cntry_txpwr,
|
|
"Allow setting tx power table of country; 0: disable (default), 1: enable.");
|
|
module_param(init_hostcmd_cfg, charp, 0);
|
|
MODULE_PARM_DESC(init_hostcmd_cfg, "Init hostcmd file name");
|
|
module_param(cfg80211_wext, int, 0660);
|
|
MODULE_PARM_DESC(cfg80211_wext,
|
|
#ifdef STA_WEXT
|
|
"Bit 0: STA WEXT; "
|
|
#endif
|
|
#ifdef UAP_WEXT
|
|
"Bit 1: UAP WEXT; "
|
|
#endif
|
|
#ifdef STA_CFG80211
|
|
"Bit 2: STA CFG80211; "
|
|
#endif
|
|
#ifdef UAP_CFG80211
|
|
"Bit 3: UAP CFG80211;"
|
|
#endif
|
|
);
|
|
module_param(wq_sched_prio, int, 0);
|
|
module_param(wq_sched_policy, int, 0);
|
|
MODULE_PARM_DESC(wq_sched_prio, "Priority of work queue");
|
|
MODULE_PARM_DESC(wq_sched_policy,
|
|
"0: SCHED_NORMAL; 1: SCHED_FIFO; 2: SCHED_RR; 3: SCHED_BATCH; 5: SCHED_IDLE");
|
|
module_param(rx_work, int, 0);
|
|
MODULE_PARM_DESC(rx_work,
|
|
"0: default; 1: Enable rx_work_queue; 2: Disable rx_work_queue");
|
|
#if defined(WIFI_DIRECT_SUPPORT)
|
|
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
module_param(p2p_enh, int, 0);
|
|
MODULE_PARM_DESC(p2p_enh, "1: Enable enhanced P2P; 0: Disable enhanced P2P");
|
|
#endif
|
|
#endif
|
|
#endif
|
|
module_param(pcie_int_mode, int, 0);
|
|
MODULE_PARM_DESC(pcie_int_mode, "0: Legacy mode; 1: MSI mode; 2: MSI-X mode");
|
|
|
|
#ifdef ANDROID_KERNEL
|
|
module_param(wakelock_timeout, int, 0);
|
|
MODULE_PARM_DESC(wakelock_timeout, "set wakelock_timeout value (ms)");
|
|
#endif
|
|
|
|
module_param(dev_cap_mask, uint, 0);
|
|
MODULE_PARM_DESC(dev_cap_mask, "Device capability mask");
|
|
|
|
module_param(pmic, int, 0);
|
|
MODULE_PARM_DESC(pmic,
|
|
"1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware");
|
|
|
|
module_param(uap_oper_ctrl, uint, 0);
|
|
MODULE_PARM_DESC(uap_oper_ctrl, "0:default; 0x20001:uap restarts on channel 6");
|
|
|
|
module_param(hs_wake_interval, int, 0660);
|
|
MODULE_PARM_DESC(hs_wake_interval,
|
|
"Host sleep wakeup interval,it will round to nearest multiple dtim*beacon_period in fw");
|
|
module_param(indication_gpio, int, 0);
|
|
MODULE_PARM_DESC(indication_gpio,
|
|
"GPIO to indicate wakeup source; high four bits: level for normal wakeup; low four bits: GPIO pin number.");
|
|
module_param(hs_mimo_switch, int, 0660);
|
|
MODULE_PARM_DESC(hs_mimo_switch,
|
|
"Dynamic MIMO-SISO switch during host sleep; 0: disable (default), 1: enable");
|
|
|
|
module_param(indrstcfg, int, 0);
|
|
MODULE_PARM_DESC(indrstcfg,
|
|
"Independent reset configuration; high byte: GPIO pin number; low byte: IR mode");
|
|
|
|
module_param(fixed_beacon_buffer, int, 0);
|
|
MODULE_PARM_DESC(fixed_beacon_buffer,
|
|
"0: allocate default buffer size; 1: allocate max buffer size.");
|
|
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
module_param(GoAgeoutTime, int, 0);
|
|
MODULE_PARM_DESC(GoAgeoutTime,
|
|
"0: use default ageout time; set Go age out time (TU 100ms)");
|
|
#endif
|
|
|
|
module_param(gtk_rekey_offload, int, 0);
|
|
MODULE_PARM_DESC(gtk_rekey_offload,
|
|
"0: disable gtk_rekey_offload; 1: enable gtk_rekey_offload (default); 2: enable gtk_rekey_offload in suspend mode only;");
|
|
|
|
module_param(multi_dtim, ushort, 0);
|
|
MODULE_PARM_DESC(multi_dtim, "DTIM interval");
|
|
|
|
module_param(inact_tmo, ushort, 0);
|
|
MODULE_PARM_DESC(inact_tmo, "IEEE ps inactivity timout value");
|
|
|
|
module_param(napi, int, 0);
|
|
MODULE_PARM_DESC(napi, "1: enable napi api; 0: disable napi");
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
module_param(dfs_offload, int, 0);
|
|
MODULE_PARM_DESC(dfs_offload, "1: enable dfs offload; 0: disable dfs offload.");
|
|
#endif
|
|
|
|
module_param(drcs_chantime_mode, int, 0);
|
|
MODULE_PARM_DESC(drcs_chantime_mode,
|
|
"0: use default value;Bit31~Bit24:Channel time for channel index0;Bit23~Bit16:mode for channel index0;Bit15~Bit8:Channel time for channel index1;Bit7~Bit0:mode for channel index1; mode:0--PM1,1--Null2Self.");
|
|
|
|
module_param(roamoffload_in_hs, int, 0);
|
|
MODULE_PARM_DESC(roamoffload_in_hs,
|
|
"1: enable fw roaming only when host suspend; 0: always enable fw roaming.");
|
|
|
|
#ifdef UAP_SUPPORT
|
|
module_param(uap_max_sta, int, 0);
|
|
MODULE_PARM_DESC(uap_max_sta, "Maximum station number for UAP/GO.");
|
|
#endif
|
|
|
|
MODULE_DESCRIPTION("M-WLAN Driver");
|
|
MODULE_AUTHOR("Marvell International Ltd.");
|
|
MODULE_VERSION(MLAN_RELEASE_VERSION);
|
|
MODULE_LICENSE("GPL");
|