blob: c4cc7ad9e9b69f04d8ccc69ce8ef1cbc05587dcc [file] [log] [blame]
/** @file moal_ioctl.c
*
* @brief This file contains ioctl function to MLAN
*
* Copyright (C) 2008-2018, 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_eth_ioctl.h"
#include "moal_sdio.h"
#ifdef UAP_SUPPORT
#include "moal_uap.h"
#endif
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#include "moal_cfg80211.h"
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
#include "moal_cfgvendor.h"
#endif
#endif
/********************************************************
Local Variables
********************************************************/
#define MRVL_TLV_HEADER_SIZE 4
/* Marvell Channel config TLV ID */
#define MRVL_CHANNELCONFIG_TLV_ID (0x0100 + 0x2a) /* 0x012a */
typedef struct _hostcmd_header {
/** Command Header : Command */
t_u16 command;
/** Command Header : Size */
t_u16 size;
/** Command Header : Sequence number */
t_u16 seq_num;
/** Command Header : Result */
t_u16 result;
/** Command action */
t_u16 action;
} hostcmd_header, *phostcmd_header;
/** Region code mapping */
typedef struct _region_code_mapping_t {
/** Region */
t_u8 region[COUNTRY_CODE_LEN];
/** Code */
t_u8 code;
} region_code_mapping_t;
#define EU_REGION_CODE 0x30
/** Region code mapping table */
static region_code_mapping_t region_code_mapping[] = {
{"US ", 0x10}, /* US FCC */
{"CA ", 0x20}, /* IC Canada */
{"SG ", 0x10}, /* Singapore */
{"EU ", 0x30}, /* ETSI */
{"AU ", 0x30}, /* Australia */
{"KR ", 0x30}, /* Republic Of Korea */
{"CN ", 0x50}, /* China */
{"JP ", 0xFF}, /* Japan special */
};
/** EEPROM Region code mapping table */
static region_code_mapping_t hw_region_code_mapping[] = {
{"US ", 0x10}, /* US FCC */
{"CA ", 0x20}, /* IC Canada */
{"KR ", 0x30}, /* Korea */
{"CN ", 0x50}, /* China */
{"ES ", 0x31}, /* Spain */
{"FR ", 0x32}, /* France */
{"JP ", 0x40}, /* Japan */
{"JP ", 0x41}, /* Japan */
};
/** Country code for ETSI */
static t_u8 eu_country_code_table[][COUNTRY_CODE_LEN] = {
"AL", "AD", "AT", "AU", "BY", "BE", "BA", "BG", "HR", "CY",
"CZ", "DK", "EE", "FI", "FR", "MK", "DE", "GR", "HU", "IS",
"IE", "IT", "KR", "LV", "LI", "LT", "LU", "MT", "MD", "MC",
"ME", "NL", "NO", "PL", "RO", "RU", "SM", "RS", "SI", "SK",
"ES", "SE", "CH", "TR", "UA", "UK", "GB"
};
/********************************************************
Global Variables
********************************************************/
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
#ifdef UAP_SUPPORT
/** Network device handlers for uAP */
extern const struct net_device_ops woal_uap_netdev_ops;
#endif
#ifdef STA_SUPPORT
/** Network device handlers for STA */
extern const struct net_device_ops woal_netdev_ops;
#endif
#endif
extern int cfg80211_wext;
int disconnect_on_suspend;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
extern int dfs_offload;
#endif
#endif
extern int roamoffload_in_hs;
/** gtk rekey offload mode */
extern int gtk_rekey_offload;
/********************************************************
Local Functions
********************************************************/
/**
* @brief This function converts region string to region code
*
* @param region_string Region string
*
* @return Region code
*/
t_u8
region_string_2_region_code(char *region_string)
{
t_u8 i;
ENTER();
for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) {
if (!memcmp(region_string,
region_code_mapping[i].region,
strlen(region_string))) {
LEAVE();
return region_code_mapping[i].code;
}
}
/* If still not found, look for code in EU country code table */
for (i = 0; i < ARRAY_SIZE(eu_country_code_table); i++) {
if (!memcmp(region_string, eu_country_code_table[i],
COUNTRY_CODE_LEN - 1)) {
PRINTM(MIOCTL, "found region code=%d in EU table\n",
EU_REGION_CODE);
LEAVE();
return EU_REGION_CODE;
}
}
/* Default is US */
LEAVE();
return region_code_mapping[0].code;
}
/**
* @brief This function converts region string to region code
*
* @param country_code Region string
*
* @return Region code
*/
t_bool
woal_is_etsi_country(t_u8 *country_code)
{
t_u8 i;
ENTER();
for (i = 0; i < ARRAY_SIZE(eu_country_code_table); i++) {
if (!memcmp(country_code, eu_country_code_table[i],
COUNTRY_CODE_LEN - 1)) {
PRINTM(MIOCTL, "found region code=%d in EU table\n",
EU_REGION_CODE);
LEAVE();
return MTRUE;
}
}
LEAVE();
return MFALSE;
}
/**
* @brief This function converts region string to region code
*
* @param region_code region code
*
* @return Region string or NULL
*/
char *
region_code_2_string(t_u8 region_code)
{
t_u8 i;
ENTER();
for (i = 0; i < ARRAY_SIZE(hw_region_code_mapping); i++) {
if (hw_region_code_mapping[i].code == region_code) {
LEAVE();
return hw_region_code_mapping[i].region;
}
}
LEAVE();
return NULL;
}
t_u8
woal_is_valid_alpha2(char *alpha2)
{
if (!alpha2 || strlen(alpha2) < 2)
return MFALSE;
if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
return MTRUE;
return MFALSE;
}
/**
* @brief Copy mc address to the mlist
*
* @param mlist A pointer to mlan_multicast_list structure
* @param mac mc address
*
* @return N/A
*/
static inline void
woal_copy_mc_addr(mlan_multicast_list *mlist, mlan_802_11_mac_addr mac)
{
int i = 0;
for (i = 0; i < mlist->num_multicast_addr; i++) {
if (!memcmp(&mlist->mac_list[i], mac, ETH_ALEN))
return;
}
if (mlist->num_multicast_addr < MLAN_MAX_MULTICAST_LIST_SIZE)
memcpy(&mlist->mac_list[mlist->num_multicast_addr], mac,
ETH_ALEN);
mlist->num_multicast_addr++;
return;
}
/**
* @brief Copy multicast table
*
* @param mlist A pointer to mlan_multicast_list structure
* @param dev A pointer to net_device structure
*
* @return Number of multicast addresses
*/
static inline int
woal_copy_mcast_addr(mlan_multicast_list *mlist, struct net_device *dev)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
struct dev_mc_list *mcptr = dev->mc_list;
int i = 0;
#else
struct netdev_hw_addr *mcptr = NULL;
#endif /* < 2.6.35 */
ENTER();
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
for (i = 0; i < dev->mc_count && mcptr; i++) {
woal_copy_mc_addr(mlist, mcptr->dmi_addr);
mcptr = mcptr->next;
}
#else
netdev_for_each_mc_addr(mcptr, dev)
woal_copy_mc_addr(mlist, mcptr->addr);
#endif /* < 2.6.35 */
LEAVE();
return mlist->num_multicast_addr;
}
/**
* @brief copy mc list from all the active interface
*
* @param handle A pointer to moal_handle
* @param mlist A pointer to multicast list
*
* @return total_mc_count
*/
static int
woal_copy_all_mc_list(moal_handle *handle, mlan_multicast_list *mlist)
{
int i;
moal_private *priv = NULL;
#ifdef STA_SUPPORT
int mc_count = 0;
#endif
ENTER();
for (i = 0; i < handle->priv_num && (priv = handle->priv[i]); i++) {
#ifdef STA_SUPPORT
if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) {
if (handle->priv[i]->media_connected == MTRUE) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
mc_count = priv->netdev->mc_count;
#else
mc_count = netdev_mc_count(priv->netdev);
#endif
if (mc_count)
woal_copy_mcast_addr(mlist,
priv->netdev);
}
}
#endif
}
PRINTM(MIOCTL, "total mc_count=%d\n", mlist->num_multicast_addr);
LEAVE();
return mlist->num_multicast_addr;
}
/**
* @brief Fill in wait queue
*
* @param priv A pointer to moal_private structure
* @param wait A pointer to wait_queue structure
* @param wait_option Wait option
*
* @return N/A
*/
static inline void
woal_fill_wait_queue(moal_private *priv, wait_queue *wait, t_u8 wait_option)
{
ENTER();
wait->start_time = jiffies;
wait->condition = MFALSE;
wait->wait_timeout = MFALSE;
switch (wait_option) {
case MOAL_NO_WAIT:
break;
case MOAL_IOCTL_WAIT:
init_waitqueue_head(&wait->wait);
break;
case MOAL_IOCTL_WAIT_TIMEOUT:
init_waitqueue_head(&wait->wait);
wait->wait_timeout = MTRUE;
break;
}
LEAVE();
return;
}
/**
* @brief Wait mlan ioctl complete
*
* @param priv A pointer to moal_private structure
* @param req A pointer to mlan_ioctl_req structure
* @param wait_option Wait option
*
* @return N/A
*/
static inline mlan_status
woal_wait_ioctl_complete(moal_private *priv, mlan_ioctl_req *req,
t_u8 wait_option)
{
mlan_status status;
wait_queue *wait = (wait_queue *)req->reserved_1;
unsigned long flags;
ENTER();
priv->phandle->ioctl_timeout = MFALSE;
switch (wait_option) {
case MOAL_NO_WAIT:
break;
case MOAL_IOCTL_WAIT:
while (wait_event_interruptible_exclusive
(wait->wait, wait->condition)
== -ERESTARTSYS && wait->retry < MAX_RETRY_CNT) {
wait->retry++;
}
break;
case MOAL_IOCTL_WAIT_TIMEOUT:
wait_event_timeout(wait->wait, wait->condition,
MOAL_IOCTL_TIMEOUT);
break;
}
spin_lock_irqsave(&priv->phandle->driver_lock, flags);
if (wait->condition == MFALSE) {
if (wait_option == MOAL_IOCTL_WAIT_TIMEOUT) {
priv->phandle->ioctl_timeout = MTRUE;
PRINTM(MMSG,
"wlan: IOCTL timeout %p id=0x%x, sub_id=0x%x, wait_option=%d, action=%d\n",
req, req->req_id, (*(t_u32 *)req->pbuf),
wait_option, (int)req->action);
} else {
PRINTM(MMSG,
"wlan: IOCTL by signal %p id=0x%x, sub_id=0x%x, wait_option=%d, action=%d\n",
req, req->req_id, (*(t_u32 *)req->pbuf),
wait_option, (int)req->action);
}
req->reserved_1 = 0;
status = MLAN_STATUS_PENDING;
} else {
status = wait->status;
}
spin_unlock_irqrestore(&priv->phandle->driver_lock, flags);
LEAVE();
return status;
}
/**
* @brief CAC period block cmd handler
*
* @param priv A pointer to moal_private structure
* @param req A pointer to mlan_ioctl_req buffer
*
* @return MTRUE/MFALSE
*/
static inline t_bool
woal_cac_period_block_cmd(moal_private *priv, pmlan_ioctl_req req)
{
mlan_status ret = MFALSE;
t_u32 sub_command;
ENTER();
if (req == NULL || req->pbuf == NULL)
goto done;
sub_command = *(t_u32 *)req->pbuf;
switch (req->req_id) {
case MLAN_IOCTL_SCAN:
if (sub_command == MLAN_OID_SCAN_NORMAL ||
sub_command == MLAN_OID_SCAN_SPECIFIC_SSID ||
sub_command == MLAN_OID_SCAN_USER_CONFIG)
ret = MTRUE;
break;
case MLAN_IOCTL_BSS:
if (sub_command == MLAN_OID_BSS_STOP ||
sub_command == MLAN_OID_BSS_CHANNEL
/* sub_command == MLAN_OID_BSS_ROLE */ )
ret = MTRUE;
#ifdef UAP_SUPPORT
else if (sub_command == MLAN_OID_UAP_BSS_CONFIG) {
mlan_ds_bss *bss = (mlan_ds_bss *)req->pbuf;
if (bss->param.bss_config.channel)
ret = MTRUE;
else
ret = MFALSE;
}
#endif
break;
case MLAN_IOCTL_RADIO_CFG:
if (sub_command == MLAN_OID_BAND_CFG)
ret = MTRUE;
break;
case MLAN_IOCTL_SNMP_MIB:
if (sub_command == MLAN_OID_SNMP_MIB_DOT11D)
ret = MTRUE;
#if defined(UAP_SUPPORT)
if (sub_command == MLAN_OID_SNMP_MIB_DOT11H)
ret = MTRUE;
#endif
break;
case MLAN_IOCTL_11D_CFG:
#ifdef STA_SUPPORT
if (sub_command == MLAN_OID_11D_CFG_ENABLE)
ret = MTRUE;
#endif
#ifdef UAP_SUPPORT
if (sub_command == MLAN_OID_11D_DOMAIN_INFO)
ret = MTRUE;
#endif
if (sub_command == MLAN_OID_11D_DOMAIN_INFO_EXT)
ret = MTRUE;
break;
case MLAN_IOCTL_MISC_CFG:
if (sub_command == MLAN_OID_MISC_REGION)
ret = MTRUE;
if (sub_command == MLAN_OID_MISC_HOST_CMD) {
phostcmd_header phostcmd;
t_u8 *ptlv_buf;
t_u16 tag, length;
phostcmd =
(phostcmd_header)((pmlan_ds_misc_cfg)req->
pbuf)->param.hostcmd.cmd;
ptlv_buf = (t_u8 *)phostcmd + sizeof(hostcmd_header);
if (phostcmd->action == MLAN_ACT_SET) {
while (ptlv_buf <
(t_u8 *)phostcmd + phostcmd->size) {
tag = *(t_u16 *)ptlv_buf;
length = *(t_u16 *)(ptlv_buf + 2);
/* Check Blocking TLV here, should add more... */
if (tag == MRVL_CHANNELCONFIG_TLV_ID) {
ret = MTRUE;
break;
}
ptlv_buf +=
(length + MRVL_TLV_HEADER_SIZE);
}
}
}
break;
case MLAN_IOCTL_11H_CFG:
/* Prevent execute more than once */
if (sub_command == MLAN_OID_11H_CHANNEL_CHECK)
ret = MTRUE;
break;
default:
ret = MFALSE;
break;
}
done:
LEAVE();
return ret;
}
/********************************************************
Global Functions
********************************************************/
/**
* @brief Send ioctl request to MLAN
*
* @param priv A pointer to moal_private structure
* @param req A pointer to mlan_ioctl_req buffer
* @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT)
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING
* -- success, otherwise fail
*/
mlan_status
woal_request_ioctl(moal_private *priv, mlan_ioctl_req *req, t_u8 wait_option)
{
wait_queue *wait = NULL;
mlan_status status;
unsigned long flags;
t_u32 sub_command = *(t_u32 *)req->pbuf;
ENTER();
if (!priv) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
if (sub_command != MLAN_OID_GET_DEBUG_INFO) {
if (priv->phandle->surprise_removed == MTRUE ||
priv->phandle->driver_state) {
PRINTM(MERROR,
"IOCTL is not allowed while the device is not present or hang\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
#if defined(SDIO_SUSPEND_RESUME)
if (priv->phandle->is_suspended == MTRUE) {
PRINTM(MERROR,
"IOCTL is not allowed while suspended\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
#endif
}
/* For MLAN_OID_MISC_HOST_CMD, action is 0, "action set" is checked later */
if ((req->action == MLAN_ACT_SET || req->action == 0) &&
priv->phandle->cac_period == MTRUE) {
/* CAC checking period left to complete jiffies */
long cac_left_jiffies;
/* cac_left_jiffies will be negative if and only if
* event MLAN_EVENT_ID_DRV_MEAS_REPORT recieved from FW
* after CAC measure period ends,
* usually this could be considered as a FW bug
*/
cac_left_jiffies = priv->phandle->cac_timer_jiffies -
(jiffies - priv->phandle->meas_start_jiffies);
#ifdef DFS_TESTING_SUPPORT
if (priv->phandle->cac_period_jiffies) {
cac_left_jiffies = priv->phandle->cac_period_jiffies -
(jiffies - priv->phandle->meas_start_jiffies);
}
#endif
if (cac_left_jiffies < 0) {
/* Avoid driver hang in FW died during CAC measure period */
priv->phandle->cac_period = MFALSE;
PRINTM(MERROR,
"CAC measure period spends longer than scheduled time "
"or meas done event never received\n");
status = MLAN_STATUS_FAILURE;
#ifdef UAP_SUPPORT
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (priv->uap_host_based && dfs_offload)
woal_cfg80211_dfs_vendor_event(priv,
event_dfs_cac_aborted,
&priv->chan);
#endif
#endif
#endif
goto done;
}
/* Check BSS START first */
if (sub_command == MLAN_OID_BSS_START) {
mlan_ds_bss *bss;
bss = (mlan_ds_bss *)req->pbuf;
/*
* Bss delay start after channel report received,
* not block the driver by delay executing. This is
* because a BSS_START cmd is always executed right
* after channel check issued.
*/
if (priv->phandle->delay_bss_start == MFALSE) {
PRINTM(MMSG,
"Received BSS Start command during CAC period, delay executing %ld seconds\n",
cac_left_jiffies / HZ);
priv->phandle->delay_bss_start = MTRUE;
memcpy(&priv->phandle->delay_ssid_bssid,
&bss->param.ssid_bssid,
sizeof(mlan_ssid_bssid));
/* TODO: return success to allow the half below of routines
* of which calling BSS start to execute
*/
status = MLAN_STATUS_SUCCESS;
goto done;
} else {
/* TODO: not blocking it, just return failure */
PRINTM(MMSG,
"Only one BSS Start command allowed for delay executing!\n");
status = MLAN_STATUS_FAILURE;
goto done;
}
}
if (woal_cac_period_block_cmd(priv, req)) {
priv->phandle->meas_wait_q_woken = MFALSE;
PRINTM(MMSG,
"CAC check is on going... Blocking Command %ld seconds\n",
cac_left_jiffies / HZ);
/* blocking timeout set to 1.5 * CAC checking period left time */
wait_event_interruptible_timeout(priv->phandle->
meas_wait_q,
priv->phandle->
meas_wait_q_woken,
cac_left_jiffies * 3 /
2);
}
}
#ifdef UAP_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
else if (priv->phandle->is_cac_timer_set &&
(req->action == MLAN_ACT_SET || req->action == 0)) {
if (woal_cac_period_block_cmd(priv, req)) {
PRINTM(MMSG,
"CAC check is on going... Blocking Command\n");
status = MLAN_STATUS_FAILURE;
goto done;
}
}
#endif
#endif
else if (priv->phandle->cac_period) {
PRINTM(MINFO, "Operation during CAC check period.\n");
}
wait = (wait_queue *)req->reserved_1;
req->bss_index = priv->bss_index;
if (wait_option)
woal_fill_wait_queue(priv, wait, wait_option);
else
req->reserved_1 = 0;
/* Call MLAN ioctl handle */
atomic_inc(&priv->phandle->ioctl_pending);
spin_lock_irqsave(&priv->phandle->ioctl_lock, flags);
status = mlan_ioctl(priv->phandle->pmlan_adapter, req);
spin_unlock_irqrestore(&priv->phandle->ioctl_lock, flags);
switch (status) {
case MLAN_STATUS_PENDING:
if (wait_option == MOAL_NO_WAIT)
PRINTM(MIOCTL, "IOCTL MOAL_NO_WAIT: %p\n", req);
else
PRINTM(MIOCTL,
"IOCTL pending: %p id=0x%x, sub_id=0x%x wait_option=%d, action=%d\n",
req, req->req_id, (*(t_u32 *)req->pbuf),
wait_option, (int)req->action);
/* Status pending, wake up main process */
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
/* Wait for completion */
if (wait_option)
status = woal_wait_ioctl_complete(priv, req,
wait_option);
break;
case MLAN_STATUS_SUCCESS:
case MLAN_STATUS_FAILURE:
case MLAN_STATUS_RESOURCE:
PRINTM(MIOCTL,
"IOCTL: %p id=0x%x, sub_id=0x%x wait_option=%d, action=%d status=%d\n",
req, req->req_id, (*(t_u32 *)req->pbuf), wait_option,
(int)req->action, status);
atomic_dec(&priv->phandle->ioctl_pending);
break;
default:
atomic_dec(&priv->phandle->ioctl_pending);
break;
}
done:
LEAVE();
return status;
}
/**
* @brief Send set MAC address request to MLAN
*
* @param priv A pointer to moal_private structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING
* -- success, otherwise fail
*/
mlan_status
woal_request_set_mac_address(moal_private *priv)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status status;
ENTER();
/* Allocate an IOCTL request buffer */
req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
status = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_MAC_ADDR;
memcpy(&bss->param.mac_addr, priv->current_addr,
sizeof(mlan_802_11_mac_addr));
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status == MLAN_STATUS_SUCCESS) {
memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN);
HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN);
} else {
PRINTM(MERROR,
"set mac address failed! status=%d, error_code=0x%x\n",
status, req->status_code);
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Send multicast list request to MLAN
*
* @param priv A pointer to moal_private structure
* @param dev A pointer to net_device structure
*
* @return N/A
*/
void
woal_request_set_multicast_list(moal_private *priv, struct net_device *dev)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status status;
int mc_count = 0;
ENTER();
/* Allocate an IOCTL request buffer */
req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
PRINTM(MERROR, "%s:Fail to allocate ioctl req buffer\n",
__func__);
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_MULTICAST_LIST;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
if (dev->flags & IFF_PROMISC) {
bss->param.multicast_list.mode = MLAN_PROMISC_MODE;
} else if (dev->flags & IFF_ALLMULTI) {
bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE;
} else {
bss->param.multicast_list.mode = MLAN_MULTICAST_MODE;
mc_count =
woal_copy_all_mc_list(priv->phandle,
&bss->param.multicast_list);
if (mc_count > MLAN_MAX_MULTICAST_LIST_SIZE)
bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE;
}
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_NO_WAIT);
if (status != MLAN_STATUS_PENDING)
kfree(req);
done:
LEAVE();
return;
}
/**
* @brief Send deauth command to MLAN
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param mac MAC address to deauthenticate
* @param reason_code reason code to deauthenticate
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_disconnect(moal_private *priv, t_u8 wait_option, t_u8 *mac,
t_u16 reason_code)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status status;
ENTER();
/* Allocate an IOCTL request buffer */
req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
status = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_STOP;
if (mac)
memcpy(&bss->param.deauth_param.mac_addr, mac,
sizeof(mlan_802_11_mac_addr));
bss->param.deauth_param.reason_code = reason_code;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
#ifdef REASSOCIATION
priv->reassoc_required = MFALSE;
#endif /* REASSOCIATION */
LEAVE();
return status;
}
#if defined(UAP_SUPPORT)
/**
* @brief Get non-global oper class
*
* @param priv Pointer to moal_private structure
* @param bw bandwidth
* @param channel channel
* @param oper_class pointer to oper_class
* @return non-global operclass
*/
int
woal_priv_get_nonglobal_operclass_by_bw_channel(moal_private *priv,
t_u8 bandwidth, t_u8 channel,
t_u8 *oper_class)
{
int ret = 0;
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_OPER_CLASS;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_GET;
misc->param.bw_chan_oper.bandwidth = bandwidth;
misc->param.bw_chan_oper.channel = channel;
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
*oper_class = misc->param.bw_chan_oper.oper_class;
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
#endif
/**
* @brief Check current uap/go connection status
* Need handle channel switch if current channel is DFS channel
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param new channel new channel
* @return N/A
*/
void
woal_check_uap_dfs_status(moal_private *priv, t_u8 wait_option,
t_u8 new_channel)
{
chan_band_info channel;
mlan_bss_info bss_info;
#if defined(UAP_SUPPORT)
IEEEtypes_ChanSwitchAnn_t *chan_switch = NULL;
IEEEtypes_ExtChanSwitchAnn_t *ext_chan_switch = NULL;
custom_ie *pcust_chansw_ie = NULL;
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
t_u8 bw = 0, oper_class = 0;
#endif
/* Get BSS information */
if (MLAN_STATUS_SUCCESS !=
woal_get_bss_info(priv, wait_option, &bss_info))
goto done;
if (MLAN_STATUS_SUCCESS !=
woal_set_get_ap_channel(priv, MLAN_ACT_GET, wait_option, &channel))
goto done;
PRINTM(MCMND, "is_11h_active=%d dfs_check_channel=%d\n",
bss_info.is_11h_active, bss_info.dfs_check_channel);
PRINTM(MCMND, "uap current channel=%d new_channel=%d\n",
channel.channel, new_channel);
#if defined(UAP_SUPPORT)
if (new_channel == channel.channel)
goto done;
if (bss_info.is_11h_active &&
(bss_info.dfs_check_channel == channel.channel)) {
if (new_channel < MAX_BG_CHANNEL) {
bw = 20;
} else {
switch (channel.bandcfg.chanWidth) {
case CHAN_BW_20MHZ:
bw = 20;
break;
case CHAN_BW_40MHZ:
bw = 40;
break;
case CHAN_BW_80MHZ:
bw = 80;
break;
default:
break;
}
}
woal_priv_get_nonglobal_operclass_by_bw_channel(priv, bw,
new_channel,
&oper_class);
PRINTM(MCMND,
"Switch the uap channel from %d to %d, oper_class=%d bw=%d\n",
channel.channel, new_channel, oper_class, bw);
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req) {
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_CUSTOM_IE;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_SET;
misc->param.cust_ie.type = TLV_TYPE_MGMT_IE;
misc->param.cust_ie.len =
sizeof(custom_ie) - MAX_IE_SIZE;
pcust_chansw_ie =
(custom_ie *)&misc->param.cust_ie.
ie_data_list[0];
pcust_chansw_ie->ie_index = 0xffff; /*Auto index */
if (!oper_class) {
pcust_chansw_ie->ie_length =
sizeof(IEEEtypes_ChanSwitchAnn_t);
pcust_chansw_ie->mgmt_subtype_mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP; /*Add IE for BEACON/probe resp */
chan_switch =
(IEEEtypes_ChanSwitchAnn_t *)
pcust_chansw_ie->ie_buffer;
chan_switch->element_id = CHANNEL_SWITCH_ANN;
chan_switch->len =
sizeof(IEEEtypes_ChanSwitchAnn_t) -
sizeof(IEEEtypes_Header_t);
chan_switch->chan_switch_mode = 1; /* STA should not transmit */
chan_switch->new_channel_num = new_channel;
chan_switch->chan_switch_count =
DEF_CHAN_SWITCH_COUNT;
DBG_HEXDUMP(MCMD_D, "CSA IE",
(t_u8 *)pcust_chansw_ie->ie_buffer,
pcust_chansw_ie->ie_length);
} else {
pcust_chansw_ie->ie_length =
sizeof(IEEEtypes_ExtChanSwitchAnn_t);
pcust_chansw_ie->mgmt_subtype_mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP; /*Add IE for BEACON/probe resp */
ext_chan_switch =
(IEEEtypes_ExtChanSwitchAnn_t *)
pcust_chansw_ie->ie_buffer;
ext_chan_switch->element_id =
EXTEND_CHANNEL_SWITCH_ANN;
ext_chan_switch->len =
sizeof(IEEEtypes_ExtChanSwitchAnn_t) -
sizeof(IEEEtypes_Header_t);
ext_chan_switch->chan_switch_mode = 1; /* STA should not transmit */
ext_chan_switch->new_channel_num = new_channel;
ext_chan_switch->chan_switch_count =
DEF_CHAN_SWITCH_COUNT;
ext_chan_switch->new_oper_class = oper_class;
DBG_HEXDUMP(MCMD_D, "ECSA IE",
(t_u8 *)pcust_chansw_ie->ie_buffer,
pcust_chansw_ie->ie_length);
}
status = woal_request_ioctl(priv, ioctl_req,
wait_option);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "Failed to set CSA IE\n");
goto done;
}
PRINTM(MCMND, "CSA/ECSA ie index=%d\n",
pcust_chansw_ie->ie_index);
priv->phandle->chsw_wait_q_woken = MFALSE;
/* wait for channel switch to complete */
wait_event_interruptible_timeout(priv->phandle->
chsw_wait_q,
priv->phandle->
chsw_wait_q_woken,
(u32)HZ *
(DEF_CHAN_SWITCH_COUNT
+ 2) * 110 / 1000);
pcust_chansw_ie->ie_index = 0xffff;
pcust_chansw_ie->mgmt_subtype_mask =
MLAN_CUSTOM_IE_DELETE_MASK;
status = woal_request_ioctl(priv, ioctl_req,
MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "Failed to clear CSA/ECSA IE\n");
}
}
}
#endif
done:
#if defined(UAP_SUPPORT)
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
#endif
return;
}
/**
* @brief Check current multi-channel connections
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param new channel new channel
*
* @return N/A
*/
void
woal_check_mc_connection(moal_private *priv, t_u8 wait_option, t_u8 new_channel)
{
moal_handle *handle = priv->phandle;
#ifdef UAP_SUPPORT
int i;
#endif
t_u16 enable = 0;
woal_mc_policy_cfg(priv, &enable, wait_option, MLAN_ACT_GET);
if (!enable)
return;
#ifdef UAP_SUPPORT
for (i = 0; i < handle->priv_num; i++) {
if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) {
if (handle->priv[i]->bss_started == MTRUE)
woal_check_uap_dfs_status(handle->priv[i],
wait_option,
new_channel);
}
}
#endif
return;
}
/**
* @brief Send bss_start command to MLAN
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param ssid_bssid A point to mlan_ssid_bssid structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_bss_start(moal_private *priv, t_u8 wait_option,
mlan_ssid_bssid *ssid_bssid)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status status;
mlan_ssid_bssid temp_ssid_bssid;
ENTER();
/* Stop the O.S. TX queue When we are roaming */
woal_stop_queue(priv->netdev);
if (priv->media_connected == MFALSE) {
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
}
memcpy(&temp_ssid_bssid, ssid_bssid, sizeof(mlan_ssid_bssid));
if (MLAN_STATUS_SUCCESS ==
woal_find_best_network(priv, wait_option, &temp_ssid_bssid))
woal_check_mc_connection(priv, wait_option,
temp_ssid_bssid.channel);
/* Allocate an IOCTL request buffer */
req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
status = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_START;
if (ssid_bssid)
memcpy(&bss->param.ssid_bssid, ssid_bssid,
sizeof(mlan_ssid_bssid));
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
#ifdef STA_CFG80211
#ifdef STA_SUPPORT
priv->assoc_status = req->status_code;
#endif
#endif
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get BSS info
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param bss_info A pointer to mlan_bss_info structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_bss_info(moal_private *priv, t_u8 wait_option, mlan_bss_info *bss_info)
{
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
PRINTM(MERROR,
"Fail to allocate the buffer for get bss_info\n");
status = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_GET_BSS_INFO;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (bss_info)
memcpy(bss_info, &info->param.bss_info,
sizeof(mlan_bss_info));
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set/Get generic IE
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param ie Information element
* @param ie_len Length of the IE
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_get_gen_ie(moal_private *priv, t_u32 action,
t_u8 *ie, int *ie_len, t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
if ((action == MLAN_ACT_GET) && (ie == NULL || ie_len == NULL)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (action == MLAN_ACT_SET && *ie_len > MAX_IE_SIZE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_GEN_IE;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = action;
misc->param.gen_ie.type = MLAN_IE_TYPE_GEN_IE;
if (action == MLAN_ACT_SET) {
misc->param.gen_ie.len = *ie_len;
if (*ie_len)
memcpy(misc->param.gen_ie.ie_data, ie, *ie_len);
}
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (action == MLAN_ACT_GET) {
*ie_len = misc->param.gen_ie.len;
if (*ie_len)
memcpy(ie, misc->param.gen_ie.ie_data, *ie_len);
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#ifdef STA_SUPPORT
/**
* @brief Set/Get retry count
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param wait_option Wait option
* @param value Retry value
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_get_retry(moal_private *priv, t_u32 action,
t_u8 wait_option, int *value)
{
mlan_ioctl_req *req = NULL;
mlan_ds_snmp_mib *mib = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
mib = (mlan_ds_snmp_mib *)req->pbuf;
mib->sub_command = MLAN_OID_SNMP_MIB_RETRY_COUNT;
req->req_id = MLAN_IOCTL_SNMP_MIB;
req->action = action;
if (action == MLAN_ACT_SET) {
if (*value < MLAN_TX_RETRY_MIN || *value > MLAN_TX_RETRY_MAX) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
mib->param.retry_count = *value;
}
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET)
*value = mib->param.retry_count;
#ifdef STA_CFG80211
/* If set is invoked from other than iw i.e iwconfig,
* wiphy retry count should be updated as well */
if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy &&
(GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
(action == MLAN_ACT_SET)) {
priv->wdev->wiphy->retry_long = (t_u8)*value;
priv->wdev->wiphy->retry_short = (t_u8)*value;
}
#endif
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get RTS threshold
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param wait_option Wait option
* @param value RTS threshold value
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_get_rts(moal_private *priv, t_u32 action, t_u8 wait_option, int *value)
{
mlan_ioctl_req *req = NULL;
mlan_ds_snmp_mib *mib = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
mib = (mlan_ds_snmp_mib *)req->pbuf;
mib->sub_command = MLAN_OID_SNMP_MIB_RTS_THRESHOLD;
req->req_id = MLAN_IOCTL_SNMP_MIB;
req->action = action;
if (action == MLAN_ACT_SET) {
if (*value < MLAN_RTS_MIN_VALUE || *value > MLAN_RTS_MAX_VALUE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
mib->param.rts_threshold = *value;
}
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET)
*value = mib->param.rts_threshold;
#ifdef STA_CFG80211
/* If set is invoked from other than iw i.e iwconfig,
* wiphy RTS threshold should be updated as well */
if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy &&
(GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
(action == MLAN_ACT_SET))
priv->wdev->wiphy->rts_threshold = *value;
#endif
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get Fragment threshold
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param wait_option Wait option
* @param value Fragment threshold value
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_get_frag(moal_private *priv, t_u32 action,
t_u8 wait_option, int *value)
{
mlan_ioctl_req *req = NULL;
mlan_ds_snmp_mib *mib = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
mib = (mlan_ds_snmp_mib *)req->pbuf;
mib->sub_command = MLAN_OID_SNMP_MIB_FRAG_THRESHOLD;
req->req_id = MLAN_IOCTL_SNMP_MIB;
req->action = action;
if (action == MLAN_ACT_SET) {
if (*value < MLAN_FRAG_MIN_VALUE ||
*value > MLAN_FRAG_MAX_VALUE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
mib->param.frag_threshold = *value;
}
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET)
*value = mib->param.frag_threshold;
#ifdef STA_CFG80211
/* If set is invoked from other than iw i.e iwconfig,
* wiphy fragment threshold should be updated as well */
if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && priv->wdev->wiphy &&
(GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
(action == MLAN_ACT_SET))
priv->wdev->wiphy->frag_threshold = *value;
#endif
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get TX power
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param power_cfg A pinter to mlan_power_cfg_t structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_get_tx_power(moal_private *priv,
t_u32 action, mlan_power_cfg_t *power_cfg)
{
mlan_ds_power_cfg *pcfg = NULL;
mlan_ioctl_req *req = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
pcfg = (mlan_ds_power_cfg *)req->pbuf;
pcfg->sub_command = MLAN_OID_POWER_CFG;
req->req_id = MLAN_IOCTL_POWER_CFG;
req->action = action;
if (action == MLAN_ACT_SET && power_cfg)
memcpy(&pcfg->param.power_cfg, power_cfg,
sizeof(mlan_power_cfg_t));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (ret == MLAN_STATUS_SUCCESS && power_cfg)
memcpy(power_cfg, &pcfg->param.power_cfg,
sizeof(mlan_power_cfg_t));
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get IEEE power management
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param disabled A pointer to disabled flag
* @param power_type IEEE power type
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_get_power_mgmt(moal_private *priv,
t_u32 action, int *disabled, int power_type,
t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pm_cfg = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
pm_cfg = (mlan_ds_pm_cfg *)req->pbuf;
pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = action;
if (action == MLAN_ACT_SET) {
PRINTM(MINFO, "PS_MODE set power disabled=%d power type=%#x\n",
*disabled, power_type);
if (*disabled)
pm_cfg->param.ps_mode = 0;
else {
/* Check not support case only (vwrq->disabled == FALSE) */
if ((power_type & MW_POWER_TYPE) == MW_POWER_TIMEOUT) {
PRINTM(MERROR,
"Setting power timeout is not supported\n");
ret = MLAN_STATUS_FAILURE;
goto done;
} else if ((power_type & MW_POWER_TYPE) ==
MW_POWER_PERIOD) {
PRINTM(MERROR,
"Setting power period is not supported\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
pm_cfg->param.ps_mode = 1;
}
}
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET)
*disabled = pm_cfg->param.ps_mode;
#ifdef STA_CFG80211
/* If set is invoked from other than iw i.e iwconfig,
* wiphy IEEE power save mode should be updated */
if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev &&
(GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
(action == MLAN_ACT_SET)) {
if (*disabled)
priv->wdev->ps = MFALSE;
else
priv->wdev->ps = MTRUE;
}
#endif
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set region code
*
* @param priv A pointer to moal_private structure
* @param region A pointer to region string
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail
*/
mlan_status
woal_set_region_code(moal_private *priv, char *region)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_misc_cfg *cfg = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
cfg = (mlan_ds_misc_cfg *)req->pbuf;
cfg->sub_command = MLAN_OID_MISC_REGION;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
cfg->param.region_code = region_string_2_region_code(region);
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get data rate
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param datarate A pointer to mlan_rate_cfg_t structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_get_data_rate(moal_private *priv,
t_u8 action, mlan_rate_cfg_t *datarate)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_rate *rate = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
rate = (mlan_ds_rate *)req->pbuf;
rate->param.rate_cfg.rate_type = MLAN_RATE_VALUE;
rate->sub_command = MLAN_OID_RATE_CFG;
req->req_id = MLAN_IOCTL_RATE;
req->action = action;
if (datarate && (action == MLAN_ACT_SET))
memcpy(&rate->param.rate_cfg, datarate,
sizeof(mlan_rate_cfg_t));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret == MLAN_STATUS_SUCCESS && datarate && action == MLAN_ACT_GET)
memcpy(datarate, &rate->param.rate_cfg,
sizeof(mlan_rate_cfg_t));
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get assoc_resp buffer
*
* @param priv A pointer to moal_private structure
* @param assoc_rsp A pointer to mlan_ds_misc_assoc_rsp structure
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_assoc_rsp(moal_private *priv, mlan_ds_misc_assoc_rsp *assoc_rsp,
t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
PRINTM(MERROR, "Fail to allocate buffer for get assoc resp\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
req->req_id = MLAN_IOCTL_MISC_CFG;
misc = (pmlan_ds_misc_cfg)req->pbuf;
misc->sub_command = MLAN_OID_MISC_ASSOC_RSP;
req->action = MLAN_ACT_GET;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS && assoc_rsp)
memcpy(assoc_rsp, &misc->param.assoc_resp,
sizeof(mlan_ds_misc_assoc_rsp));
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
/**
* @brief Send get FW info request to MLAN
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param fw_info FW information
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_request_get_fw_info(moal_private *priv, t_u8 wait_option,
mlan_fw_info *fw_info)
{
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info;
mlan_status status;
ENTER();
memset(priv->current_addr, 0xff, ETH_ALEN);
/* Allocate an IOCTL request buffer */
req = (mlan_ioctl_req *)
woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
status = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
info->sub_command = MLAN_OID_GET_FW_INFO;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
priv->phandle->fw_release_number = info->param.fw_info.fw_ver;
priv->phandle->fw_ecsa_enable = info->param.fw_info.ecsa_enable;
priv->phandle->fw_getlog_enable =
info->param.fw_info.getlog_enable;
priv->phandle->fw_roaming_support =
info->param.fw_info.fw_roaming_support;
if (priv->current_addr[0] == 0xff)
memcpy(priv->current_addr,
&info->param.fw_info.mac_addr,
sizeof(mlan_802_11_mac_addr));
memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN);
if (fw_info)
memcpy(fw_info, &info->param.fw_info,
sizeof(mlan_fw_info));
DBG_HEXDUMP(MCMD_D, "mac", priv->current_addr, 6);
} else
PRINTM(MERROR,
"get fw info failed! status=%d, error_code=0x%x\n",
status, req->status_code);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get current channel of active interface
*
* @param priv A pointer to moal_private
* @param channel A pointer to chan_band_info structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
woal_get_active_intf_channel(moal_private *priv, chan_band_info * channel)
{
moal_handle *handle = priv->phandle;
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 woal_get_sta_channel(handle->priv[i],
MOAL_IOCTL_WAIT,
channel);
}
#endif
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) {
if (handle->priv[i]->bss_started == MTRUE)
return woal_set_get_ap_channel(handle->priv[i],
MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
channel);
}
#endif
}
return MLAN_STATUS_FAILURE;
}
#ifdef STA_SUPPORT
/**
* @brief Send get ext cap info request to MLAN
*
* @param priv A pointer to moal_private structure
* @param buf data buffer
* @param len data buffer length
*
* @return number of bytes of extended capability -- success, otherwise error
*/
int
woal_request_extcap(moal_private *priv, t_u8 *buf, t_u8 len)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *cfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (buf == NULL || len <= 0 || len < sizeof(ExtCap_t)) {
ret = -EINVAL;
goto out;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto out;
}
cfg = (mlan_ds_misc_cfg *)req->pbuf;
cfg->sub_command = MLAN_OID_MISC_EXT_CAP_CFG;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_GET;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto out;
}
memset(buf, 0, len);
memcpy(buf, &cfg->param.ext_cap, sizeof(ExtCap_t));
ret = sizeof(ExtCap_t);
out:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
#ifdef PROC_DEBUG
/**
* @brief Get debug info
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param debug_info A pointer to mlan_debug_info structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_debug_info(moal_private *priv, t_u8 wait_option,
mlan_debug_info *debug_info)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_get_info *info = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) +
sizeof(mlan_debug_info));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_GET_DEBUG_INFO;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (debug_info) {
memcpy(debug_info, &info->param.debug_info,
sizeof(mlan_debug_info));
}
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
#endif /* PROC_DEBUG */
#if defined(STA_WEXT) || defined(UAP_WEXT)
/**
* @brief host command ioctl function
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
* @return 0 --success, otherwise fail
*/
int
woal_host_command(moal_private *priv, struct iwreq *wrq)
{
HostCmd_Header cmd_header;
int ret = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Sanity check */
if (wrq->u.data.pointer == NULL) {
PRINTM(MERROR, "hostcmd IOCTL corrupt data\n");
ret = -EINVAL;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
memset(&cmd_header, 0, sizeof(cmd_header));
/* get command header */
if (copy_from_user
(&cmd_header, wrq->u.data.pointer, sizeof(HostCmd_Header))) {
PRINTM(MERROR, "copy from user failed: Host command header\n");
ret = -EFAULT;
goto done;
}
misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size);
PRINTM(MINFO, "Host command len = %u\n", misc->param.hostcmd.len);
if (!misc->param.hostcmd.len ||
misc->param.hostcmd.len > MLAN_SIZE_OF_CMD_BUFFER) {
PRINTM(MERROR, "Invalid data buffer length\n");
ret = -EINVAL;
goto done;
}
/* get the whole command from user */
if (copy_from_user
(misc->param.hostcmd.cmd, wrq->u.data.pointer,
woal_le16_to_cpu(cmd_header.size))) {
PRINTM(MERROR, "copy from user failed\n");
ret = -EFAULT;
goto done;
}
misc->sub_command = MLAN_OID_MISC_HOST_CMD;
req->req_id = MLAN_IOCTL_MISC_CFG;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (copy_to_user
(wrq->u.data.pointer, (t_u8 *)misc->param.hostcmd.cmd,
misc->param.hostcmd.len)) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = misc->param.hostcmd.len;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
#if defined(WIFI_DIRECT_SUPPORT) || defined(UAP_SUPPORT)
/**
* @brief host command ioctl function
*
* @param dev A pointer to net_device structure
* @param req A pointer to ifreq structure
* @return 0 --success, otherwise fail
*/
/********* format of ifr_data *************/
/* buf_len + Hostcmd_body */
/* buf_len: 4 bytes */
/* the length of the buf which */
/* can be used to return data */
/* to application */
/* Hostcmd_body */
/*******************************************/
int
woal_hostcmd_ioctl(struct net_device *dev, struct ifreq *req)
{
moal_private *priv = (moal_private *)netdev_priv(dev);
t_u32 buf_len = 0;
HostCmd_Header cmd_header;
int ret = 0;
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *ioctl_req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Sanity check */
if (req->ifr_data == NULL) {
PRINTM(MERROR, "uap_hostcmd_ioctl() corrupt data\n");
ret = -EFAULT;
goto done;
}
if (copy_from_user(&buf_len, req->ifr_data, sizeof(buf_len))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
memset(&cmd_header, 0, sizeof(cmd_header));
/* get command header */
if (copy_from_user
(&cmd_header, req->ifr_data + sizeof(buf_len),
sizeof(HostCmd_Header))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
PRINTM(MINFO, "Host command len = %d\n",
woal_le16_to_cpu(cmd_header.size));
if (woal_le16_to_cpu(cmd_header.size) > MLAN_SIZE_OF_CMD_BUFFER) {
ret = -EINVAL;
goto done;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size);
/* get the whole command from user */
if (copy_from_user
(misc->param.hostcmd.cmd, req->ifr_data + sizeof(buf_len),
misc->param.hostcmd.len)) {
PRINTM(MERROR, "copy from user failed\n");
ret = -EFAULT;
goto done;
}
misc->sub_command = MLAN_OID_MISC_HOST_CMD;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (misc->param.hostcmd.len > buf_len) {
PRINTM(MERROR,
"buf_len is too small, resp_len=%d, buf_len=%d\n",
(int)misc->param.hostcmd.len, (int)buf_len);
ret = -EFAULT;
goto done;
}
if (copy_to_user
(req->ifr_data + sizeof(buf_len), (t_u8 *)misc->param.hostcmd.cmd,
misc->param.hostcmd.len)) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
#endif
/**
* @brief CUSTOM_IE ioctl handler
*
* @param dev A pointer to net_device structure
* @param req A pointer to ifreq structure
* @return 0 --success, otherwise fail
*/
int
woal_custom_ie_ioctl(struct net_device *dev, struct ifreq *req)
{
moal_private *priv = (moal_private *)netdev_priv(dev);
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_custom_ie *custom_ie = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
gfp_t flag;
ENTER();
/* Sanity check */
if (req->ifr_data == NULL) {
PRINTM(MERROR, "woal_custom_ie_ioctl() corrupt data\n");
ret = -EFAULT;
goto done;
}
flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
custom_ie = kzalloc(sizeof(mlan_ds_misc_custom_ie), flag);
if (!custom_ie) {
ret = -ENOMEM;
goto done;
}
if (copy_from_user
(custom_ie, req->ifr_data, sizeof(mlan_ds_misc_custom_ie))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_CUSTOM_IE;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
if ((custom_ie->len == 0)||(custom_ie->len ==
sizeof(custom_ie->ie_data_list[0].
ie_index)))
ioctl_req->action = MLAN_ACT_GET;
else
ioctl_req->action = MLAN_ACT_SET;
memcpy(&misc->param.cust_ie, custom_ie, sizeof(mlan_ds_misc_custom_ie));
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (ioctl_req->action == MLAN_ACT_GET) {
if (copy_to_user
(req->ifr_data, &misc->param.cust_ie,
sizeof(mlan_ds_misc_custom_ie))) {
PRINTM(MERROR, "Copy to user failed!\n");
ret = -EFAULT;
goto done;
}
} else if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) {
/* send a separate error code to indicate error from driver */
ret = EFAULT;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
kfree(custom_ie);
LEAVE();
return ret;
}
/**
* @brief send raw data packet ioctl function
*
* @param dev A pointer to net_device structure
* @param req A pointer to ifreq structure
* @return 0 --success, otherwise fail
*/
int
woal_send_host_packet(struct net_device *dev, struct ifreq *req)
{
moal_private *priv = (moal_private *)netdev_priv(dev);
t_u32 packet_len = 0;
int ret = 0;
pmlan_buffer pmbuf = NULL;
mlan_status status;
IEEEtypes_ActionCategory_e *action_cat;
t_u8 *action;
struct tx_status_info *tx_info = NULL;
struct sk_buff *skb = NULL;
unsigned long flags;
struct ieee80211_mgmt *mgmt_frame;
t_u8 *pdata;
ENTER();
if (!priv || !priv->phandle) {
PRINTM(MERROR, "priv or handle is NULL\n");
ret = -EFAULT;
goto done;
}
/* Sanity check */
if (req->ifr_data == NULL) {
PRINTM(MERROR, "woal_send_host_packet() corrupt data\n");
ret = -EFAULT;
goto done;
}
if (copy_from_user(&packet_len, req->ifr_data, sizeof(packet_len))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
#define PACKET_HEADER_LEN 8
#define MV_ETH_FRAME_LEN 1514
if (packet_len > MV_ETH_FRAME_LEN) {
PRINTM(MERROR, "Invalid packet length %d\n", packet_len);
ret = -EFAULT;
goto done;
}
pmbuf = woal_alloc_mlan_buffer(priv->phandle,
(int)(MLAN_MIN_DATA_HEADER_LEN +
(int)packet_len +
PACKET_HEADER_LEN));
if (!pmbuf) {
PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
ret = -ENOMEM;
goto done;
}
pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
/* get whole packet and header */
if (copy_from_user
(pmbuf->pbuf + pmbuf->data_offset,
req->ifr_data + sizeof(packet_len),
PACKET_HEADER_LEN + packet_len)) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
woal_free_mlan_buffer(priv->phandle, pmbuf);
goto done;
}
pmbuf->data_len = PACKET_HEADER_LEN + packet_len;
pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
pmbuf->bss_index = priv->bss_index;
action_cat =
(IEEEtypes_ActionCategory_e *)&pmbuf->pbuf[pmbuf->data_offset +
MLAN_ACTION_FRAME_CATEGORY_OFFSET];
action = &pmbuf->pbuf[pmbuf->data_offset +
MLAN_ACTION_FRAME_ACTION_OFFSET];
if (*action_cat == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM &&
*action == 0x1) {
pmbuf->flags |= MLAN_BUF_FLAG_TX_STATUS;
if (!priv->tx_seq_num)
priv->tx_seq_num++;
pmbuf->tx_seq_num = priv->tx_seq_num++;
tx_info = kzalloc(sizeof(struct tx_status_info), GFP_ATOMIC);
if (tx_info) {
skb = alloc_skb(pmbuf->data_len, GFP_ATOMIC);
if (skb) {
mgmt_frame = (struct ieee80211_mgmt *)skb->data;
//Copy DA,SA and BSSID feilds.
pdata = (t_u8 *)mgmt_frame->da;
memcpy(pdata,
&pmbuf->pbuf[pmbuf->data_offset + 14],
3 * MLAN_MAC_ADDR_LENGTH);
//Copy packet data starting from category field
pdata = (t_u8 *)&mgmt_frame->u.action.category;
memcpy(pdata, (t_u8 *)action_cat,
packet_len - sizeof(t_u16) -
sizeof(moal_wlan_802_11_header));
/* packet_len - size of frame length field send from app */
skb_put(skb, packet_len - sizeof(t_u16));
spin_lock_irqsave(&priv->tx_stat_lock, flags);
tx_info->tx_skb = skb;
tx_info->tx_seq_num = pmbuf->tx_seq_num;
tx_info->tx_cookie = 0;
INIT_LIST_HEAD(&tx_info->link);
list_add_tail(&tx_info->link,
&priv->tx_stat_queue);
spin_unlock_irqrestore(&priv->tx_stat_lock,
flags);
} else {
kfree(tx_info);
tx_info = NULL;
}
}
}
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
switch (status) {
case MLAN_STATUS_PENDING:
atomic_inc(&priv->phandle->tx_pending);
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
break;
case MLAN_STATUS_SUCCESS:
woal_free_mlan_buffer(priv->phandle, pmbuf);
break;
case MLAN_STATUS_FAILURE:
default:
woal_free_mlan_buffer(priv->phandle, pmbuf);
ret = -EFAULT;
break;
}
done:
LEAVE();
return ret;
}
#if defined(UAP_WEXT)
/**
* @brief Set/Get CUSTOM_IE ioctl handler
*
* @param priv A pointer to moal_private structure
* @param mask Mask to set or clear from caller
* @param ie IE buffer to set for beacon
* @param ie_len Length of the IE
*
* @return 0 --success, otherwise fail
*/
int
woal_set_get_custom_ie(moal_private *priv, t_u16 mask, t_u8 *ie, int ie_len)
{
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_custom_ie *misc_ie = NULL;
int ret = 0;
custom_ie *pcust_bcn_ie = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
LEAVE();
return -ENOMEM;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_CUSTOM_IE;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_SET;
misc_ie = &misc->param.cust_ie;
misc_ie->type = TLV_TYPE_MGMT_IE;
misc_ie->len = (sizeof(custom_ie) - MAX_IE_SIZE) + ie_len;
pcust_bcn_ie = misc_ie->ie_data_list;
pcust_bcn_ie->ie_index = 0xffff;
pcust_bcn_ie->mgmt_subtype_mask = mask;
pcust_bcn_ie->ie_length = ie_len;
memcpy(pcust_bcn_ie->ie_buffer, ie, ie_len);
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS)
ret = -EFAULT;
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
#endif /* defined(HOST_TXRX_MGMT_FRAME) && defined(UAP_WEXT) */
/**
* @brief TDLS configuration ioctl handler
*
* @param dev A pointer to net_device structure
* @param req A pointer to ifreq structure
* @return 0 --success, otherwise fail
*/
int
woal_tdls_config_ioctl(struct net_device *dev, struct ifreq *req)
{
moal_private *priv = (moal_private *)netdev_priv(dev);
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_tdls_config *tdls_data = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
gfp_t flag;
ENTER();
/* Sanity check */
if (req->ifr_data == NULL) {
PRINTM(MERROR, "woal_tdls_config_ioctl() corrupt data\n");
ret = -EFAULT;
goto done;
}
flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
tdls_data = kzalloc(sizeof(mlan_ds_misc_tdls_config), flag);
if (!tdls_data) {
ret = -ENOMEM;
goto done;
}
if (copy_from_user
(tdls_data, req->ifr_data, sizeof(mlan_ds_misc_tdls_config))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_TDLS_CONFIG;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
if (tdls_data->tdls_action == WLAN_TDLS_DISCOVERY_REQ
|| tdls_data->tdls_action == WLAN_TDLS_LINK_STATUS)
ioctl_req->action = MLAN_ACT_GET;
else
ioctl_req->action = MLAN_ACT_SET;
memcpy(&misc->param.tdls_config, tdls_data,
sizeof(mlan_ds_misc_tdls_config));
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (tdls_data->tdls_action == WLAN_TDLS_DISCOVERY_REQ
|| tdls_data->tdls_action == WLAN_TDLS_LINK_STATUS) {
if (copy_to_user(req->ifr_data, &misc->param.tdls_config,
sizeof(mlan_ds_misc_tdls_config))) {
PRINTM(MERROR, "Copy to user failed!\n");
ret = -EFAULT;
goto done;
}
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
kfree(tdls_data);
LEAVE();
return ret;
}
/**
* @brief ioctl function get BSS type
*
* @param dev A pointer to net_device structure
* @param req A pointer to ifreq structure
* @return 0 --success, otherwise fail
*/
int
woal_get_bss_type(struct net_device *dev, struct ifreq *req)
{
int ret = 0;
moal_private *priv = (moal_private *)netdev_priv(dev);
int bss_type;
ENTER();
bss_type = (int)priv->bss_type;
if (copy_to_user(req->ifr_data, &bss_type, sizeof(int))) {
PRINTM(MINFO, "Copy to user failed!\n");
ret = -EFAULT;
}
LEAVE();
return ret;
}
#if defined(STA_SUPPORT) && defined(UAP_SUPPORT)
/**
* @brief Swithces BSS role of interface
*
* @param priv A pointer to moal_private structure
* @param action Action: set or get
* @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT)
* @param bss_role A pointer to bss role
*
* @return 0 --success, otherwise fail
*/
mlan_status
woal_bss_role_cfg(moal_private *priv, t_u8 action,
t_u8 wait_option, t_u8 *bss_role)
{
int ret = 0;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
struct net_device *dev = priv->netdev;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_ROLE;
req->req_id = MLAN_IOCTL_BSS;
req->action = action;
if (action == MLAN_ACT_SET) {
if (priv->bss_role == *bss_role) {
PRINTM(MWARN, "BSS is in desired role already\n");
goto done;
} else {
bss->param.bss_role = *bss_role;
}
}
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (action == MLAN_ACT_GET) {
*bss_role = bss->param.bss_role;
} else {
/* Update moal_private */
priv->bss_role = *bss_role;
if (priv->bss_type == MLAN_BSS_TYPE_UAP)
priv->bss_type = MLAN_BSS_TYPE_STA;
else if (priv->bss_type == MLAN_BSS_TYPE_STA)
priv->bss_type = MLAN_BSS_TYPE_UAP;
if (*bss_role == MLAN_BSS_ROLE_UAP) {
/* Switch: STA -> uAP */
/* Setup the OS Interface to our functions */
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29)
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
#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 */
} else if (*bss_role == MLAN_BSS_ROLE_STA) {
/* Switch: uAP -> STA */
/* Setup the OS Interface to our functions */
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29)
dev->do_ioctl = woal_do_ioctl;
dev->set_multicast_list = woal_set_multicast_list;
#else
dev->netdev_ops = &woal_netdev_ops;
#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 /* STA_WEXT */
}
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#if defined(STA_WEXT) || defined(UAP_WEXT)
/**
* @brief Set/Get BSS role
*
* @param priv A pointer to moal_private structure
* @param wrq A pointer to iwreq structure
*
* @return 0 --success, otherwise fail
*/
int
woal_set_get_bss_role(moal_private *priv, struct iwreq *wrq)
{
int ret = 0;
int bss_role = 0;
t_u8 action = MLAN_ACT_GET;
ENTER();
if (wrq->u.data.length) {
if (copy_from_user(&bss_role, wrq->u.data.pointer, sizeof(int))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
}
if ((bss_role != MLAN_BSS_ROLE_STA &&
bss_role != MLAN_BSS_ROLE_UAP)
#if defined(WIFI_DIRECT_SUPPORT)
|| (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT)
#endif
) {
PRINTM(MWARN, "Invalid BSS role\n");
ret = -EINVAL;
goto done;
}
if (bss_role == GET_BSS_ROLE(priv)) {
PRINTM(MWARN, "Already BSS is in desired role\n");
ret = -EINVAL;
goto done;
}
action = MLAN_ACT_SET;
/* Reset interface */
woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE);
}
if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv,
action, MOAL_IOCTL_WAIT,
(t_u8 *)&bss_role)) {
ret = -EFAULT;
goto done;
}
if (!wrq->u.data.length) {
if (copy_to_user(wrq->u.data.pointer, &bss_role, sizeof(int))) {
ret = -EFAULT;
goto done;
}
wrq->u.data.length = 1;
} else {
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
if (IS_STA_OR_UAP_CFG80211(cfg80211_wext))
woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT);
#endif
/* Initialize private structures */
woal_init_priv(priv, MOAL_IOCTL_WAIT);
/* Enable interfaces */
netif_device_attach(priv->netdev);
woal_start_queue(priv->netdev);
}
done:
LEAVE();
return ret;
}
#endif /* STA_WEXT || UAP_WEXT */
#endif /* STA_SUPPORT && UAP_SUPPORT */
#if defined(SDIO_SUSPEND_RESUME)
#endif
/**
* @brief Set/Get DTIM period
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param wait_option Wait option
* @param value DTIM period
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_get_dtim_period(moal_private *priv,
t_u32 action, t_u8 wait_option, t_u8 *value)
{
mlan_ioctl_req *req = NULL;
mlan_ds_snmp_mib *mib = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
mib = (mlan_ds_snmp_mib *)req->pbuf;
mib->sub_command = MLAN_OID_SNMP_MIB_DTIM_PERIOD;
req->req_id = MLAN_IOCTL_SNMP_MIB;
req->action = action;
if (action == MLAN_ACT_SET)
mib->param.dtim_period = *value;
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET)
*value = (t_u8)mib->param.dtim_period;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get Host Sleep parameters
*
* @param priv A pointer to moal_private structure
* @param action Action: set or get
* @param wait_option Wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT)
* @param hscfg A pointer to mlan_ds_hs_cfg structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_get_hs_params(moal_private *priv, t_u16 action, t_u8 wait_option,
mlan_ds_hs_cfg *hscfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_pm_cfg *pmcfg = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
pmcfg = (mlan_ds_pm_cfg *)req->pbuf;
pmcfg->sub_command = MLAN_OID_PM_CFG_HS_CFG;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = action;
if (action == MLAN_ACT_SET)
memcpy(&pmcfg->param.hs_cfg, hscfg, sizeof(mlan_ds_hs_cfg));
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS) {
if (hscfg && action == MLAN_ACT_GET) {
memcpy(hscfg, &pmcfg->param.hs_cfg,
sizeof(mlan_ds_hs_cfg));
}
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get wakeup reason
*
* @param priv A pointer to moal_private structure
* @param wakeup_reason A pointer to mlan_ds_hs_wakeup_reason structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_wakeup_reason(moal_private *priv,
mlan_ds_hs_wakeup_reason *wakeup_reason)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_pm_cfg *pmcfg = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
pmcfg = (mlan_ds_pm_cfg *)req->pbuf;
pmcfg->sub_command = MLAN_OID_PM_HS_WAKEUP_REASON;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret == MLAN_STATUS_SUCCESS) {
wakeup_reason->hs_wakeup_reason =
pmcfg->param.wakeup_reason.hs_wakeup_reason;
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Cancel Host Sleep configuration
*
* @param priv A pointer to moal_private structure
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING,
* or MLAN_STATUS_FAILURE
*/
mlan_status
woal_cancel_hs(moal_private *priv, t_u8 wait_option)
{
moal_handle *handle = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_hs_cfg hscfg;
int i;
ENTER();
if (!priv) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
handle = priv->phandle;
/* Cancel Host Sleep */
hscfg.conditions = HOST_SLEEP_CFG_CANCEL;
hscfg.is_invoke_hostcmd = MTRUE;
ret = woal_set_get_hs_params(priv, MLAN_ACT_SET, wait_option, &hscfg);
if (roamoffload_in_hs) {
/*Disable firmware roaming */
woal_enable_fw_roaming(priv, 0);
}
if (handle->fw_roam_enable == ROAM_OFFLOAD_WITH_BSSID ||
handle->fw_roam_enable == ROAM_OFFLOAD_WITH_SSID ||
handle->fw_roam_enable == AUTO_RECONNECT)
woal_config_fw_roaming(priv, ROAM_OFFLOAD_RESUME_CFG, NULL);
if (handle->fw_roam_enable == AUTO_RECONNECT)
woal_set_clear_pmk(priv, MLAN_ACT_CLEAR);
#if defined(SDIO_SUSPEND_RESUME)
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
if (GTK_REKEY_OFFLOAD_SUSPEND == gtk_rekey_offload) {
PRINTM(MIOCTL,
"Cancel Host Sleep... clear gtk rekey offload of FW\n");
for (i = 0; i < handle->priv_num; i++) {
if (handle->priv[i] && handle->priv[i]->gtk_data_ready) {
PRINTM(MCMND, "clear GTK in resume\n");
woal_set_rekey_data(handle->priv[i], NULL,
MLAN_ACT_CLEAR);
}
}
}
#endif
LEAVE();
return ret;
}
/** @brief This function config fw roaming parameters
*
* @param priv A Pointer to the moal_private structure
* @return MTRUE or MFALSE
*/
int
woal_set_fw_roaming_params(moal_private *priv)
{
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
woal_roam_offload_cfg roam_offload_cfg;
t_u8 zero[MLAN_MAX_KEY_LENGTH] = { 0 };
/*Enable fw roaming */
woal_config_fw_roaming(priv, ROAM_OFFLOAD_ENABLE, NULL);
/*Download fw roaming parameters */
woal_config_fw_roaming(priv, ROAM_OFFLOAD_PARAM_CFG,
&priv->phandle->fw_roam_params);
/*Download userset passphrase key and current connection's PMK */
if (!priv->phandle->fw_roam_params.userset_passphrase) {
woal_set_clear_pmk(priv, MLAN_ACT_SET);
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
req->req_id = MLAN_IOCTL_SEC_CFG;
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE;
sec->multi_passphrase = 1;
req->action = MLAN_ACT_SET;
/*Copy user set passphrase */
memcpy((char *)sec->param.roam_passphrase,
(char *)priv->phandle->ssid_passphrase,
MAX_SEC_SSID_NUM * sizeof(mlan_ds_passphrase));
roam_offload_cfg.userset_passphrase =
priv->phandle->fw_roam_params.userset_passphrase;
if (memcmp(priv->pmk.pmk, zero, MLAN_MAX_KEY_LENGTH)) {
/*Download current connection PMK */
if (priv->pmk_saved) {
woal_set_clear_pmk(priv, MLAN_ACT_SET);
priv->pmk_saved = false;
}
}
/*Set userset to mlan adapter */
woal_config_fw_roaming(priv, ROAM_OFFLOAD_ENABLE, &roam_offload_cfg);
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (ret)
goto done;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/** @brief This function enable/disable fw roaming
*
* @param priv A Pointer to the moal_private structure
* @param enable Enable/disable fw roaming
* @return MTRUE or MFALSE
*/
int
woal_enable_fw_roaming(moal_private *priv, int data)
{
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_roam_offload *roam = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
int ret = 0;
ENTER();
if (!priv || !priv->phandle) {
PRINTM(MERROR, "priv or handle is null\n");
ret = -EFAULT;
goto done;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
if (!data && !priv->phandle->fw_roam_enable) {
PRINTM(MIOCTL, "Fw roaming already disabled\n");
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_ROAM_OFFLOAD;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
roam = (mlan_ds_misc_roam_offload *) & misc->param.roam_offload;
roam->aplist.ap_num = 0;
/* SET operation */
ioctl_req->action = MLAN_ACT_SET;
roam->enable = data;
roam->config_mode = ROAM_OFFLOAD_ENABLE;
if (roamoffload_in_hs && data) {
priv->phandle->fw_roam_enable = data;
goto done;
}
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
priv->phandle->fw_roam_enable = data;
if (!data) {
memset((char *)&priv->phandle->fw_roam_params, 0,
sizeof(woal_roam_offload_cfg));
memset((char *)&priv->phandle->ssid_passphrase, 0,
MAX_SEC_SSID_NUM * sizeof(mlan_ds_passphrase));
} else if (priv->media_connected && priv->pmk_saved) {
woal_set_clear_pmk(priv, MLAN_ACT_SET);
priv->pmk_saved = false;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
#if defined(SDIO_SUSPEND_RESUME)
/** @brief This function enables the host sleep
*
* @param priv A Pointer to the moal_private structure
* @return MTRUE or MFALSE
*/
int
woal_enable_hs(moal_private *priv)
{
mlan_ds_hs_cfg hscfg;
moal_handle *handle = NULL;
int hs_actived = MFALSE;
int timeout = 0;
int i;
#ifdef SDIO_SUSPEND_RESUME
mlan_ds_ps_info pm_info;
#endif
ENTER();
if (priv == NULL) {
PRINTM(MERROR, "Invalid priv\n");
goto done;
}
handle = priv->phandle;
if (handle->hs_activated == MTRUE) {
PRINTM(MIOCTL, "HS Already actived\n");
hs_actived = MTRUE;
goto done;
}
for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
if (handle->priv[i] &&
(GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) {
if (disconnect_on_suspend &&
handle->priv[i]->media_connected == MTRUE) {
PRINTM(MIOCTL, "disconnect on suspend\n");
woal_disconnect(handle->priv[i], MOAL_NO_WAIT,
NULL, DEF_DEAUTH_REASON_CODE);
}
}
if (handle->priv[i]) {
PRINTM(MIOCTL, "woal_delba_all on priv[%d]\n", i);
woal_delba_all(handle->priv[i], MOAL_IOCTL_WAIT);
}
}
#if defined(STA_CFG80211) && defined(UAP_CFG80211)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
if (priv->phandle->is_remain_timer_set) {
woal_cancel_timer(&priv->phandle->remain_timer);
woal_remain_timer_func(priv->phandle);
}
/* cancel pending remain on channel */
if (priv->phandle->remain_on_channel) {
t_u8 channel_status;
moal_private *remain_priv =
priv->phandle->priv[priv->phandle->remain_bss_index];
if (remain_priv) {
woal_cfg80211_remain_on_channel_cfg(remain_priv,
MOAL_NO_WAIT, MTRUE,
&channel_status,
NULL, 0, 0);
if (priv->phandle->cookie) {
cfg80211_remain_on_channel_expired(
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
remain_priv->
netdev,
#else
remain_priv->
wdev,
#endif
priv->
phandle->
cookie,
&priv->
phandle->
chan,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
priv->
phandle->
channel_type,
#endif
GFP_ATOMIC);
priv->phandle->cookie = 0;
}
}
priv->phandle->remain_on_channel = MFALSE;
}
#endif
#endif
#ifdef STA_SUPPORT
woal_reconfig_bgscan(priv->phandle);
#endif
if (roamoffload_in_hs && handle->fw_roam_enable) {
woal_set_fw_roaming_params(priv);
}
if (handle->fw_roam_enable == ROAM_OFFLOAD_WITH_BSSID ||
handle->fw_roam_enable == ROAM_OFFLOAD_WITH_SSID ||
handle->fw_roam_enable == AUTO_RECONNECT) {
woal_config_fw_roaming(priv, ROAM_OFFLOAD_SUSPEND_CFG, NULL);
if (priv->phandle->fw_roam_enable == AUTO_RECONNECT)
woal_set_clear_pmk(priv, MLAN_ACT_SET);
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
if (GTK_REKEY_OFFLOAD_SUSPEND == gtk_rekey_offload) {
PRINTM(MIOCTL,
"Host Sleep enabled... set gtk rekey offload to FW\n");
for (i = 0; i < handle->priv_num; i++) {
if (handle->priv[i] && handle->priv[i]->gtk_data_ready) {
PRINTM(MCMND, "set GTK before suspend\n");
woal_set_rekey_data(handle->priv[i],
&handle->priv[i]->
gtk_rekey_data,
MLAN_ACT_SET);
}
}
}
#endif
/* Enable Host Sleep */
handle->hs_activate_wait_q_woken = MFALSE;
memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg));
hscfg.is_invoke_hostcmd = MTRUE;
if (woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_NO_WAIT, &hscfg) ==
MLAN_STATUS_FAILURE) {
PRINTM(MIOCTL, "IOCTL request HS enable failed\n");
goto done;
}
timeout = wait_event_interruptible_timeout(handle->hs_activate_wait_q,
handle->
hs_activate_wait_q_woken,
HS_ACTIVE_TIMEOUT);
sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func);
if ((handle->hs_activated == MTRUE) || (handle->is_suspended == MTRUE)) {
PRINTM(MCMND, "suspend success! force=%u skip=%u\n",
handle->hs_force_count, handle->hs_skip_count);
hs_actived = MTRUE;
}
#ifdef SDIO_SUSPEND_RESUME
else {
handle->suspend_fail = MTRUE;
woal_get_pm_info(priv, &pm_info);
if (pm_info.is_suspend_allowed == MTRUE) {
#ifdef MMC_PM_FUNC_SUSPENDED
woal_wlan_is_suspended(priv->phandle);
#endif
handle->hs_force_count++;
PRINTM(MCMND, "suspend allowed! force=%u skip=%u\n",
handle->hs_force_count, handle->hs_skip_count);
hs_actived = MTRUE;
}
}
#endif /* SDIO_SUSPEND_RESUME */
sdio_release_host(((struct sdio_mmc_card *)handle->card)->func);
if (hs_actived != MTRUE) {
handle->hs_skip_count++;
#ifdef SDIO_SUSPEND_RESUME
PRINTM(MCMND,
"suspend skipped! timeout=%d allow=%d force=%u skip=%u\n",
timeout, (int)pm_info.is_suspend_allowed,
handle->hs_force_count, handle->hs_skip_count);
#else
PRINTM(MCMND, "suspend skipped! timeout=%d skip=%u\n",
timeout, handle->hs_skip_count);
#endif
woal_cancel_hs(priv, MOAL_NO_WAIT);
}
done:
LEAVE();
return hs_actived;
}
#endif
#ifdef CONFIG_PROC_FS
/**
* @brief This function send soft_reset command to firmware
*
* @param handle A pointer to moal_handle structure
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING on success,
* otherwise failure code
*/
mlan_status
woal_request_soft_reset(moal_handle *handle)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
ENTER();
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_SOFT_RESET;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
ret = woal_request_ioctl(woal_get_priv
(handle, MLAN_BSS_ROLE_ANY), req,
MOAL_IOCTL_WAIT);
}
handle->surprise_removed = MTRUE;
woal_sched_timeout(5);
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif /* CONFIG_PROC_FS */
/**
* @brief Set wapi enable
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param enable MTRUE or MFALSE
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_wapi_enable(moal_private *priv, t_u8 wait_option, t_u32 enable)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_WAPI_ENABLED;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.wapi_enabled = enable;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get version
*
* @param handle A pointer to moal_handle structure
* @param version A pointer to version buffer
* @param max_len max length of version buffer
*
* @return N/A
*/
void
woal_get_version(moal_handle *handle, char *version, int max_len)
{
union {
t_u32 l;
t_u8 c[4];
} ver;
char fw_ver[32];
ENTER();
ver.l = handle->fw_release_number;
snprintf(fw_ver, sizeof(fw_ver), "%u.%u.%u.p%u",
ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
snprintf(version, max_len, handle->driver_version, fw_ver);
LEAVE();
}
#if defined(STA_WEXT) || defined(UAP_WEXT)
/**
* @brief Get Driver Version
*
* @param priv A pointer to moal_private structure
* @param req A pointer to ifreq structure
*
* @return 0 --success, otherwise fail
*/
int
woal_get_driver_version(moal_private *priv, struct ifreq *req)
{
struct iwreq *wrq = (struct iwreq *)req;
int len;
char buf[MLAN_MAX_VER_STR_LEN];
ENTER();
woal_get_version(priv->phandle, buf, sizeof(buf) - 1);
len = strlen(buf);
if (wrq->u.data.pointer) {
if (copy_to_user(wrq->u.data.pointer, buf, len)) {
PRINTM(MERROR, "Copy to user failed\n");
LEAVE();
return -EFAULT;
}
wrq->u.data.length = len;
}
PRINTM(MINFO, "MOAL VERSION: %s\n", buf);
LEAVE();
return 0;
}
/**
* @brief Get extended driver version
*
* @param priv A pointer to moal_private structure
* @param ireq A pointer to ifreq structure
*
* @return 0 --success, otherwise fail
*/
int
woal_get_driver_verext(moal_private *priv, struct ifreq *ireq)
{
struct iwreq *wrq = (struct iwreq *)ireq;
mlan_ds_get_info *info = NULL;
mlan_ioctl_req *req = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_GET_VER_EXT;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
if (!wrq->u.data.flags) {
info->param.ver_ext.version_str_sel =
*((int *)(wrq->u.name + SUBCMD_OFFSET));
} else {
if (copy_from_user
(&info->param.ver_ext.version_str_sel, wrq->u.data.pointer,
sizeof(info->param.ver_ext.version_str_sel))) {
PRINTM(MERROR, "Copy from user failed\n");
ret = -EFAULT;
goto done;
} else {
if (((t_s32)(info->param.ver_ext.version_str_sel)) < 0) {
PRINTM(MERROR, "Invalid arguments!\n");
ret = -EINVAL;
goto done;
}
}
}
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (wrq->u.data.pointer) {
if (copy_to_user
(wrq->u.data.pointer, info->param.ver_ext.version_str,
strlen(info->param.ver_ext.version_str))) {
PRINTM(MERROR, "Copy to user failed\n");
ret = -EFAULT;
goto done;
}
wrq->u.data.length = strlen(info->param.ver_ext.version_str);
}
PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n",
info->param.ver_ext.version_str);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
#ifdef DEBUG_LEVEL1
/**
* @brief Set driver debug bit masks to mlan in order to enhance performance
*
* @param priv A pointer to moal_private structure
* @param drvdbg Driver debug level
*
* @return 0 --success, otherwise fail
*/
int
woal_set_drvdbg(moal_private *priv, t_u32 drvdbg)
{
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_DRVDBG;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
misc->param.drvdbg = drvdbg;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
/**
* @brief Mgmt frame forward registration
*
* @param priv A pointer to moal_private structure
* @param action Action: set or get
* @param pmgmt_subtype_mask A Pointer to mgmt frame subtype mask
* @param wait_option wait option (MOAL_IOCTL_WAIT or MOAL_NO_WAIT)
*
* @return 0 --success, otherwise fail
*/
int
woal_reg_rx_mgmt_ind(moal_private *priv, t_u16 action,
t_u32 *pmgmt_subtype_mask, t_u8 wait_option)
{
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
int ret = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_RX_MGMT_IND;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = action;
misc->param.mgmt_subtype_mask = *pmgmt_subtype_mask;
if (req->action == MLAN_ACT_SET)
memcpy(&misc->param.mgmt_subtype_mask,
pmgmt_subtype_mask,
sizeof(misc->param.mgmt_subtype_mask));
ret = woal_request_ioctl(priv, req, wait_option);
if (req->action == MLAN_ACT_GET)
memcpy(pmgmt_subtype_mask, &misc->param.mgmt_subtype_mask,
sizeof(misc->param.mgmt_subtype_mask));
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get Transmit beamforming capabilities
*
* @param priv A pointer to moal_private structure
* @param action Action: set or get
* @param tx_bf_cap A pointer to tx_buf_cap buffer
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_get_tx_bf_cap(moal_private *priv, t_u16 action, t_u32 *tx_bf_cap)
{
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *bf_cfg = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
if (!tx_bf_cap) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bf_cfg = (mlan_ds_11n_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_11N_CFG;
bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP;
req->action = action;
if (action == MLAN_ACT_SET)
bf_cfg->param.tx_bf_cap = *tx_bf_cap;
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_SUCCESS) {
goto done;
}
if (action == MLAN_ACT_GET)
*tx_bf_cap = bf_cfg->param.tx_bf_cap;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set/Get Transmit beamforming configuration
*
* @param priv A pointer to moal_private structure
* @param action Action: set or get
* @param tx_bf_cfg A pointer to tx_bf_cfg structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_get_tx_bf_cfg(moal_private *priv, t_u16 action,
mlan_ds_11n_tx_bf_cfg *tx_bf_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *bf_cfg = NULL;
ENTER();
/* Sanity test */
if (tx_bf_cfg == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bf_cfg = (mlan_ds_11n_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_11N_CFG;
bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CFG;
req->action = action;
memcpy(&bf_cfg->param.tx_bf, tx_bf_cfg, sizeof(mlan_ds_11n_tx_bf_cfg));
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (action == MLAN_ACT_GET)
memcpy(tx_bf_cfg, &bf_cfg->param.tx_bf,
sizeof(mlan_ds_11n_tx_bf_cfg));
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Handle ioctl resp
*
* @param priv Pointer to moal_private structure
* @param req Pointer to mlan_ioctl_req structure
*
* @return N/A
*/
void
woal_process_ioctl_resp(moal_private *priv, mlan_ioctl_req *req)
{
ENTER();
if (priv == NULL) {
LEAVE();
return;
}
switch (req->req_id) {
case MLAN_IOCTL_GET_INFO:
#ifdef STA_WEXT
#ifdef STA_SUPPORT
if (IS_STA_WEXT(cfg80211_wext) &&
GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)
woal_ioctl_get_info_resp(priv,
(mlan_ds_get_info *)req->pbuf);
#endif
#endif
#ifdef UAP_WEXT
#ifdef UAP_SUPPORT
if (IS_UAP_WEXT(cfg80211_wext) &&
GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)
woal_ioctl_get_uap_info_resp(priv,
(mlan_ds_get_info *)req->
pbuf);
#endif
#endif
break;
#ifdef STA_WEXT
#ifdef STA_SUPPORT
case MLAN_IOCTL_BSS:
if (IS_STA_WEXT(cfg80211_wext) &&
GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)
woal_ioctl_get_bss_resp(priv, (mlan_ds_bss *)req->pbuf);
break;
#endif
#endif
case MLAN_IOCTL_MISC_CFG:
woal_ioctl_get_misc_conf(priv, (mlan_ds_misc_cfg *)req->pbuf);
default:
break;
}
LEAVE();
return;
}
/**
* @brief Get PM info
*
* @param priv A pointer to moal_private structure
* @param pm_info A pointer to mlan_ds_ps_info structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_pm_info(moal_private *priv, mlan_ds_ps_info *pm_info)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_pm_cfg *pmcfg = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
PRINTM(MERROR, "Fail to alloc mlan_ds_pm_cfg buffer\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
pmcfg = (mlan_ds_pm_cfg *)req->pbuf;
pmcfg->sub_command = MLAN_OID_PM_INFO;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret == MLAN_STATUS_SUCCESS) {
if (pm_info) {
memcpy(pm_info, &pmcfg->param.ps_info,
sizeof(mlan_ds_ps_info));
}
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get Deep Sleep
*
* @param priv Pointer to the moal_private driver data struct
* @param data Pointer to return deep_sleep setting
*
* @return 0 --success, otherwise fail
*/
int
woal_get_deep_sleep(moal_private *priv, t_u32 *data)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pm = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
pm = (mlan_ds_pm_cfg *)req->pbuf;
pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = MLAN_ACT_GET;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
*data = pm->param.auto_deep_sleep.auto_ds;
*(data + 1) = pm->param.auto_deep_sleep.idletime;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set Deep Sleep
*
* @param priv Pointer to the moal_private driver data struct
* @param wait_option wait option
* @param bdeep_sleep TRUE--enalbe deepsleep, FALSE--disable deepsleep
* @param idletime Idle time for optimized PS API
*
* @return 0 --success, otherwise fail
*/
int
woal_set_deep_sleep(moal_private *priv, t_u8 wait_option, BOOLEAN bdeep_sleep,
t_u16 idletime)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_pm_cfg *pm = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
pm = (mlan_ds_pm_cfg *)req->pbuf;
pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP;
req->req_id = MLAN_IOCTL_PM_CFG;
req->action = MLAN_ACT_SET;
if (bdeep_sleep == MTRUE) {
PRINTM(MIOCTL, "Deep Sleep: sleep\n");
pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON;
if (idletime)
pm->param.auto_deep_sleep.idletime = idletime;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) {
ret = -EFAULT;
goto done;
}
} else {
PRINTM(MIOCTL, "%lu : Deep Sleep: wakeup\n", jiffies);
pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) {
ret = -EFAULT;
goto done;
}
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Cancel CAC period block
*
* @param priv A pointer to moal_private structure
*
* @return N/A
*/
void
woal_cancel_cac_block(moal_private *priv)
{
ENTER();
/* if during CAC period, wake up wait queue */
if (priv->phandle->cac_period == MTRUE) {
priv->phandle->cac_period = MFALSE;
/* Make sure Chan Report is cancelled */
woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT);
priv->phandle->meas_start_jiffies = 0;
if (priv->phandle->delay_bss_start == MTRUE)
priv->phandle->delay_bss_start = MFALSE;
if (priv->phandle->meas_wait_q_woken == MFALSE) {
priv->phandle->meas_wait_q_woken = MTRUE;
wake_up_interruptible(&priv->phandle->meas_wait_q);
}
#ifdef UAP_SUPPORT
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (priv->uap_host_based && dfs_offload)
woal_cfg80211_dfs_vendor_event(priv,
event_dfs_cac_aborted,
&priv->chan);
#endif
#endif
#endif
}
LEAVE();
}
/** MEAS report timeout value in seconds */
/**
* @brief Issue MLAN_OID_11H_CHANNEL_CHECK ioctl
*
* @param priv Pointer to the moal_private driver data struct
* @param wait_option wait option
*
* @return 0 --success, otherwise fail
*/
int
woal_11h_channel_check_ioctl(moal_private *priv, t_u8 wait_option)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_11h_cfg *ds_11hcfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
chan_band_info chan;
chan_band_info uapchan;
ENTER();
if (priv->skip_cac) {
LEAVE();
return ret;
}
/* Skip sending request/report query when DFS_REPEATER_MODE is on. This
* would get rid of CAC timers before starting BSSes in DFS_REPEATER_MODE
*/
if (priv->phandle->dfs_repeater_mode) {
LEAVE();
return ret;
}
if (woal_is_any_interface_active(priv->phandle)) {
/* When any other interface is active
* Get rid of CAC timer when drcs is disabled */
t_u16 enable = 0;
ret = woal_mc_policy_cfg(priv, &enable, wait_option,
MLAN_ACT_GET);
if (!enable) {
LEAVE();
return ret;
} else {
woal_get_active_intf_channel(priv, &chan);
woal_set_get_ap_channel(priv, MLAN_ACT_GET,
MOAL_IOCTL_WAIT, &uapchan);
if (chan.channel != uapchan.channel) {
if (uapchan.is_dfs_chan) {
PRINTM(MERROR,
"DFS channel is not allowed when another connection exists on different channel\n");
PRINTM(MERROR,
"Another connection's channel=%d, dfs channel=%d\n",
chan.channel, uapchan.channel);
return -EINVAL;
} else {
//check if we need move first uap0 from DFS channel to new non dfs channel
woal_check_mc_connection(priv,
wait_option,
uapchan.
channel);
}
}
}
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf;
ds_11hcfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK;
ds_11hcfg->param.chan_rpt_req.host_based = MFALSE;
req->req_id = MLAN_IOCTL_11H_CFG;
req->action = MLAN_ACT_SET;
/* Send Channel Check command and wait until the report is ready */
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
goto done;
}
/* set flag from here */
priv->phandle->cac_period = MTRUE;
priv->phandle->meas_start_jiffies = jiffies;
priv->phandle->cac_timer_jiffies =
ds_11hcfg->param.chan_rpt_req.millisec_dwell_time * HZ / 1000;
#ifdef UAP_SUPPORT
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (priv->uap_host_based && dfs_offload)
woal_cfg80211_dfs_vendor_event(priv, event_dfs_cac_started,
&priv->chan);
#endif
#endif
#endif
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Issue MLAN_OID_11H_CHAN_REPORT_REQUEST ioctl to cancel dozer
*
* @param priv Pointer to the moal_private driver data struct
* @param wait_option wait option
*
* @return 0 --success, otherwise fail
*/
int
woal_11h_cancel_chan_report_ioctl(moal_private *priv, t_u8 wait_option)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_11h_cfg *ds_11hcfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf;
ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_REPORT_REQUEST;
req->req_id = MLAN_IOCTL_11H_CFG;
req->action = MLAN_ACT_SET;
ds_11hcfg->param.chan_rpt_req.millisec_dwell_time = 0;
/* Send Channel Check command and wait until the report is ready */
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief set remain channel
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param pchan A pointer to mlan_ds_remain_chan structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_remain_channel_ioctl(moal_private *priv, t_u8 wait_option,
mlan_ds_remain_chan *pchan)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_radio_cfg *radio_cfg = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
radio_cfg->sub_command = MLAN_OID_REMAIN_CHAN_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
req->action = MLAN_ACT_SET;
memcpy(&radio_cfg->param.remain_chan, pchan,
sizeof(mlan_ds_remain_chan));
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS) {
memcpy(pchan, &radio_cfg->param.remain_chan,
sizeof(mlan_ds_remain_chan));
PRINTM(MCMND,
"Remain_chan_cfg: remove=%d, channel=%d, remain_period=%d\n",
pchan->remove, pchan->channel, pchan->remain_period);
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#if defined(WIFI_DIRECT_SUPPORT)
/**
* @brief set/get wifi direct mode
*
* @param priv A pointer to moal_private structure
* @param action set or get
* @param mode A pointer to wifi direct mode
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_wifi_direct_mode_cfg(moal_private *priv, t_u16 action, t_u16 *mode)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
ENTER();
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_WIFI_DIRECT_MODE;
req->req_id = MLAN_IOCTL_BSS;
req->action = action;
if (action == MLAN_ACT_SET)
bss->param.wfd_mode = *mode;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret == MLAN_STATUS_SUCCESS) {
*mode = bss->param.wfd_mode;
PRINTM(MIOCTL, "ACT=%d, wifi_direct_mode=%d\n", action, *mode);
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set p2p config
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param p2p_config A pointer to mlan_ds_wifi_direct_config structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_p2p_config(moal_private *priv, t_u32 action,
mlan_ds_wifi_direct_config *p2p_config)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc_cfg = NULL;
ENTER();
if (!p2p_config) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
misc_cfg = (mlan_ds_misc_cfg *)req->pbuf;
misc_cfg->sub_command = MLAN_OID_MISC_WIFI_DIRECT_CONFIG;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = action;
memcpy(&misc_cfg->param.p2p_config, p2p_config,
sizeof(mlan_ds_wifi_direct_config));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret == MLAN_STATUS_SUCCESS) {
if (action == MLAN_ACT_GET)
memcpy(p2p_config, &misc_cfg->param.p2p_config,
sizeof(mlan_ds_wifi_direct_config));
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif /* WIFI_DIRECT_SUPPORT */
#ifdef STA_SUPPORT
/**
* @brief Get STA Channel Info
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param channel A pointer to channel info
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_sta_channel(moal_private *priv, t_u8 wait_option,
chan_band_info * channel)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
PRINTM(MERROR, "woal_get_sta_channel req alloc fail\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_CHAN_INFO;
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 && channel)
memcpy(channel, &(bss->param.sta_channel), sizeof(*channel));
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get RSSI info
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param signal A pointer tp mlan_ds_get_signal structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_signal_info(moal_private *priv, t_u8 wait_option,
mlan_ds_get_signal *signal)
{
int ret = 0;
mlan_ds_get_info *info = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
info = (mlan_ds_get_info *)req->pbuf;
info->sub_command = MLAN_OID_GET_SIGNAL;
info->param.signal.selector = ALL_RSSI_INFO_MASK;
req->req_id = MLAN_IOCTL_GET_INFO;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (signal)
memcpy(signal, &info->param.signal,
sizeof(mlan_ds_get_signal));
#ifdef STA_WEXT
if (IS_STA_WEXT(cfg80211_wext)) {
if (info->param.signal.selector & BCN_RSSI_AVG_MASK)
priv->w_stats.qual.level =
info->param.signal.bcn_rssi_avg;
if (info->param.signal.selector & BCN_NF_AVG_MASK)
priv->w_stats.qual.noise =
info->param.signal.bcn_nf_avg;
}
#endif
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get scan table
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param scan_resp A pointer to mlan_scan_resp structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_scan_table(moal_private *priv, t_u8 wait_option,
mlan_scan_resp *scan_resp)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_scan *scan = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (!scan_resp) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
scan = (mlan_ds_scan *)req->pbuf;
scan->sub_command = MLAN_OID_SCAN_NORMAL;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_GET;
memcpy((void *)&scan->param.scan_resp, (void *)scan_resp,
sizeof(mlan_scan_resp));
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS) {
if (scan_resp) {
memcpy(scan_resp, &scan->param.scan_resp,
sizeof(mlan_scan_resp));
}
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Request a scan
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param req_ssid A pointer to mlan_802_11_ssid structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_request_scan(moal_private *priv,
t_u8 wait_option, mlan_802_11_ssid *req_ssid)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_handle *handle = priv->phandle;
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_scan *scan = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) {
PRINTM(MERROR, "Acquire semaphore error, request_scan\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
handle->scan_pending_on_block = MTRUE;
handle->scan_priv = priv;
/* Allocate an IOCTL request buffer */
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (ioctl_req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)ioctl_req->pbuf;
if (req_ssid && req_ssid->ssid_len != 0) {
/* Specific SSID scan */
ioctl_req->req_id = MLAN_IOCTL_SCAN;
ioctl_req->action = MLAN_ACT_SET;
scan->sub_command = MLAN_OID_SCAN_SPECIFIC_SSID;
memcpy(scan->param.scan_req.scan_ssid.ssid,
req_ssid->ssid,
MIN(MLAN_MAX_SSID_LENGTH, req_ssid->ssid_len));
scan->param.scan_req.scan_ssid.ssid_len =
MIN(MLAN_MAX_SSID_LENGTH, req_ssid->ssid_len);
} else {
/* Normal scan */
ioctl_req->req_id = MLAN_IOCTL_SCAN;
ioctl_req->action = MLAN_ACT_SET;
scan->sub_command = MLAN_OID_SCAN_NORMAL;
}
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, ioctl_req, wait_option);
if (status == MLAN_STATUS_FAILURE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
if (ret == MLAN_STATUS_FAILURE) {
handle->scan_pending_on_block = MFALSE;
handle->scan_priv = NULL;
MOAL_REL_SEMAPHORE(&handle->async_sem);
}
LEAVE();
return ret;
}
/**
* @brief Change Adhoc Channel
*
* @param priv A pointer to moal_private structure
* @param channel The channel to be set.
* @param wait_option wait_option
*
* @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail
*/
mlan_status
woal_change_adhoc_chan(moal_private *priv, int channel, t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_bss_info bss_info;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
memset(&bss_info, 0, sizeof(bss_info));
/* Get BSS information */
if (MLAN_STATUS_SUCCESS !=
woal_get_bss_info(priv, wait_option, &bss_info)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (bss_info.bss_mode == MLAN_BSS_MODE_INFRA) {
ret = MLAN_STATUS_SUCCESS;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Get current channel */
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_IBSS_CHANNEL;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (bss->param.bss_chan.channel == (unsigned int)channel) {
ret = MLAN_STATUS_SUCCESS;
goto done;
}
PRINTM(MINFO, "Updating Channel from %d to %d\n",
(int)bss->param.bss_chan.channel, channel);
if (bss_info.media_connected != MTRUE) {
ret = MLAN_STATUS_SUCCESS;
goto done;
}
/* Do disonnect */
bss->sub_command = MLAN_OID_BSS_STOP;
memset((t_u8 *)&bss->param.bssid, 0, ETH_ALEN);
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
/* Do specific SSID scanning */
if (MLAN_STATUS_SUCCESS !=
woal_request_scan(priv, wait_option, &bss_info.ssid)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Start/Join Adhoc network */
bss->sub_command = MLAN_OID_BSS_START;
memset(&bss->param.ssid_bssid, 0, sizeof(mlan_ssid_bssid));
memcpy(&bss->param.ssid_bssid.ssid, &bss_info.ssid,
sizeof(mlan_802_11_ssid));
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Find the best network to associate
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_find_best_network(moal_private *priv, t_u8 wait_option,
mlan_ssid_bssid *ssid_bssid)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u8 *mac = 0;
ENTER();
if (!ssid_bssid) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
bss->sub_command = MLAN_OID_BSS_FIND_BSS;
memcpy(&bss->param.ssid_bssid, ssid_bssid, sizeof(mlan_ssid_bssid));
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_SUCCESS) {
memcpy(ssid_bssid, &bss->param.ssid_bssid,
sizeof(mlan_ssid_bssid));
mac = (t_u8 *)&ssid_bssid->bssid;
PRINTM(MINFO, "Find network: ssid=%s, " MACSTR ", idx=%d\n",
ssid_bssid->ssid.ssid, MAC2STR(mac),
(int)ssid_bssid->idx);
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Check if AP channel is valid for STA Region
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_11d_check_ap_channel(moal_private *priv, t_u8 wait_option,
mlan_ssid_bssid *ssid_bssid)
{
mlan_ioctl_req *req = NULL;
mlan_ds_bss *bss = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u8 *mac = 0;
ENTER();
if (!ssid_bssid) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Fill request buffer */
bss = (mlan_ds_bss *)req->pbuf;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_GET;
bss->sub_command = MLAN_OID_BSS_11D_CHECK_CHANNEL;
memcpy(&bss->param.ssid_bssid, ssid_bssid, sizeof(mlan_ssid_bssid));
mac = (t_u8 *)&ssid_bssid->bssid;
PRINTM(MINFO, "ssid=%s, " MACSTR ", idx=%d\n",
ssid_bssid->ssid.ssid, MAC2STR(mac), (int)ssid_bssid->idx);
/* Send IOCTL request to MLAN */
ret = woal_request_ioctl(priv, req, wait_option);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Get authentication mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param auth_mode A pointer to authentication mode
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_auth_mode(moal_private *priv, t_u8 wait_option, t_u32 *auth_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS && auth_mode)
*auth_mode = sec->param.auth_mode;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get encrypt mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param encrypt_mode A pointer to encrypt mode
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_encrypt_mode(moal_private *priv, t_u8 wait_option, t_u32 *encrypt_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS && encrypt_mode)
*encrypt_mode = sec->param.encrypt_mode;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Get WPA enable
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param enable A pointer to wpa enable status
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_get_wpa_enable(moal_private *priv, t_u8 wait_option, t_u32 *enable)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status == MLAN_STATUS_SUCCESS && enable)
*enable = sec->param.wpa_enabled;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set authentication mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param auth_mode Authentication mode
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_auth_mode(moal_private *priv, t_u8 wait_option, t_u32 auth_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.auth_mode = auth_mode;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set encrypt mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param encrypt_mode Encryption mode
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_encrypt_mode(moal_private *priv, t_u8 wait_option, t_u32 encrypt_mode)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.encrypt_mode = encrypt_mode;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set wpa enable
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param enable MTRUE or MFALSE
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_wpa_enable(moal_private *priv, t_u8 wait_option, t_u32 enable)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.wpa_enabled = enable;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief enable wep key
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_enable_wep_key(moal_private *priv, t_u8 wait_option)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_SET;
sec->param.encrypt_key.key_disable = MFALSE;
sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT;
sec->param.encrypt_key.is_current_wep_key = MTRUE;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Request user scan
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param scan_cfg A pointer to wlan_user_scan_config structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_request_userscan(moal_private *priv,
t_u8 wait_option, wlan_user_scan_cfg *scan_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_scan *scan = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
moal_handle *handle = priv->phandle;
ENTER();
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) {
PRINTM(MERROR, "Acquire semaphore error, request_scan\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
handle->scan_pending_on_block = MTRUE;
handle->scan_priv = priv;
/* Allocate an IOCTL request buffer */
ioctl_req =
woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) +
sizeof(wlan_user_scan_cfg));
if (ioctl_req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)ioctl_req->pbuf;
scan->sub_command = MLAN_OID_SCAN_USER_CONFIG;
ioctl_req->req_id = MLAN_IOCTL_SCAN;
ioctl_req->action = MLAN_ACT_SET;
memcpy(scan->param.user_scan.scan_cfg_buf, scan_cfg,
sizeof(wlan_user_scan_cfg));
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, ioctl_req, wait_option);
if (status == MLAN_STATUS_FAILURE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
if (ret == MLAN_STATUS_FAILURE) {
handle->scan_pending_on_block = MFALSE;
handle->scan_priv = NULL;
MOAL_REL_SEMAPHORE(&handle->async_sem);
}
LEAVE();
return ret;
}
/**
* @brief woal_get_scan_config
*
* @param priv A pointer to moal_private structure
* @param scan_cfg A pointer to scan_cfg structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_scan_config(moal_private *priv, mlan_scan_cfg *scan_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_scan *scan = NULL;
mlan_ioctl_req *req = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)req->pbuf;
scan->sub_command = MLAN_OID_SCAN_CONFIG;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_GET;
memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret == MLAN_STATUS_SUCCESS && scan_cfg)
memcpy(scan_cfg, &scan->param.scan_cfg, sizeof(mlan_scan_cfg));
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief set scan time
*
* @param priv A pointer to moal_private structure
* @param active_scan_time Active scan time
* @param passive_scan_time Passive scan time
* @param specific_scan_time Specific scan time
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_scan_time(moal_private *priv, t_u16 active_scan_time,
t_u16 passive_scan_time, t_u16 specific_scan_time)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_scan *scan = NULL;
mlan_ioctl_req *req = NULL;
mlan_scan_cfg scan_cfg;
ENTER();
memset(&scan_cfg, 0, sizeof(scan_cfg));
if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)req->pbuf;
scan->sub_command = MLAN_OID_SCAN_CONFIG;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_SET;
memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg));
scan_cfg.scan_time.active_scan_time = active_scan_time;
scan_cfg.scan_time.specific_scan_time = specific_scan_time;
scan_cfg.scan_time.passive_scan_time = passive_scan_time;
PRINTM(MIOCTL, "Set specific=%d, active=%d, passive=%d\n",
(int)active_scan_time, (int)passive_scan_time,
(int)specific_scan_time);
memcpy(&scan->param.scan_cfg, &scan_cfg, sizeof(mlan_scan_cfg));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief request scan
*
* @param priv A pointer to moal_private structure
* @param scan_cfg A pointer to wlan_user_scan_cfg structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_do_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_handle *handle = priv->phandle;
ENTER();
if (handle->scan_pending_on_block == MTRUE) {
PRINTM(MINFO, "scan already in processing...\n");
LEAVE();
return ret;
}
#ifdef REASSOCIATION
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) {
PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n");
LEAVE();
return -EBUSY;
}
#endif /* REASSOCIATION */
priv->report_scan_result = MTRUE;
if (!scan_cfg)
ret = woal_request_scan(priv, MOAL_NO_WAIT, NULL);
else
ret = woal_request_userscan(priv, MOAL_NO_WAIT, scan_cfg);
#ifdef REASSOCIATION
MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
#endif
LEAVE();
return ret;
}
/**
* @brief Cancel pending scan
*
* @param priv A pointer to moal_private structure
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_cancel_scan(moal_private *priv, t_u8 wait_option)
{
mlan_ioctl_req *req = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_handle *handle = priv->phandle;
moal_private *scan_priv = handle->scan_priv;
#ifdef STA_CFG80211
unsigned long flags;
#endif
/* If scan is in process, cancel the scan command */
if (!handle->scan_pending_on_block || !scan_priv)
return ret;
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_SET;
((mlan_ds_scan *)req->pbuf)->sub_command = MLAN_OID_SCAN_CANCEL;
ret = woal_request_ioctl(scan_priv, req, wait_option);
handle->scan_pending_on_block = MFALSE;
MOAL_REL_SEMAPHORE(&handle->async_sem);
#ifdef STA_CFG80211
spin_lock_irqsave(&handle->scan_req_lock, flags);
if (IS_STA_CFG80211(cfg80211_wext) && handle->scan_request) {
/** some supplicant can not handle SCAN abort event */
if (scan_priv->bss_type == MLAN_BSS_TYPE_STA)
woal_cfg80211_scan_done(handle->scan_request, MTRUE);
else
woal_cfg80211_scan_done(handle->scan_request, MFALSE);
handle->scan_request = NULL;
}
spin_unlock_irqrestore(&handle->scan_req_lock, flags);
#endif
/* add 10ms delay, incase firmware delay 0x7f event after scan cancel command response */
woal_sched_timeout(10);
handle->scan_priv = NULL;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
return ret;
}
/**
* @brief find ssid in scan_table
*
* @param priv A pointer to moal_private
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE
*/
int
woal_find_essid(moal_private *priv, mlan_ssid_bssid *ssid_bssid,
t_u8 wait_option)
{
int ret = 0;
mlan_scan_resp scan_resp;
struct timeval t;
ENTER();
if (MLAN_STATUS_SUCCESS !=
woal_get_scan_table(priv, wait_option, &scan_resp)) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
#ifdef STA_CFG80211
if (priv->ft_pre_connect) {
/** skip check the scan age out */
ret = woal_find_best_network(priv, wait_option, ssid_bssid);
LEAVE();
return ret;
}
#endif
do_gettimeofday(&t);
/** scan result timeout value */
#define SCAN_RESULT_AGEOUT 10
if (t.tv_sec > (scan_resp.age_in_secs + SCAN_RESULT_AGEOUT)) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
ret = woal_find_best_network(priv, wait_option, ssid_bssid);
LEAVE();
return ret;
}
/**
* @brief auto reconnection configure
*
* @param priv Pointer to moal_private structure
* @param cfg_mode configure mode
* @param roam_offload_cfg Pointer to woal_roam_offload_cfg structure
*
* @return Number of bytes written, negative for failure.
*/
mlan_status
woal_config_fw_roaming(moal_private *priv, t_u8 cfg_mode,
woal_roam_offload_cfg * roam_offload_cfg)
{
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_roam_offload *roam = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
int ret = 0;
ENTER();
if (!priv || !priv->phandle) {
PRINTM(MERROR, "priv or handle is null\n");
ret = -EFAULT;
goto done;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_ROAM_OFFLOAD;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
roam = (mlan_ds_misc_roam_offload *) & misc->param.roam_offload;
roam->aplist.ap_num = 0;
ioctl_req->action = MLAN_ACT_SET;
roam->enable = priv->phandle->fw_roam_enable;
roam->config_mode = cfg_mode;
if ((roam->config_mode == ROAM_OFFLOAD_ENABLE) && roam_offload_cfg) {
roam->userset_passphrase = roam_offload_cfg->userset_passphrase;
if (roam->userset_passphrase)
roam->enable = 0;
}
if (roam->config_mode == ROAM_OFFLOAD_PARAM_CFG) {
memcpy((t_u8 *)&roam->bssid_reconnect,
(t_u8 *)&roam_offload_cfg->bssid, MLAN_MAC_ADDR_LENGTH);
if (roam_offload_cfg->ssid_list.ssid_num) {
memcpy((t_u8 *)&roam->ssid_list,
(t_u8 *)&roam_offload_cfg->ssid_list,
sizeof(mlan_ds_misc_ssid_list));
}
if (roam_offload_cfg->black_list.ap_num) {
memcpy((t_u8 *)&roam->black_list,
(t_u8 *)&roam_offload_cfg->black_list,
sizeof(mlan_ds_misc_roam_offload_aplist));
}
roam->trigger_condition = roam_offload_cfg->trigger_condition;
roam->retry_count = roam_offload_cfg->retry_count;
if (roam_offload_cfg->rssi_param_set_flag) {
roam->para_rssi.set_flag = 1;
roam->para_rssi.max_rssi = roam_offload_cfg->max_rssi;
roam->para_rssi.min_rssi = roam_offload_cfg->min_rssi;
roam->para_rssi.step_rssi = roam_offload_cfg->step_rssi;
}
if (roam_offload_cfg->band_rssi_flag) {
roam->band_rssi_flag = roam_offload_cfg->band_rssi_flag;
memcpy((t_u8 *)&roam->band_rssi,
(t_u8 *)&roam_offload_cfg->band_rssi,
sizeof(mlan_ds_misc_band_rssi));
}
if (roam_offload_cfg->bgscan_set_flag) {
roam->bgscan_set_flag =
roam_offload_cfg->bgscan_set_flag;
memcpy((t_u8 *)&roam->bgscan_cfg,
(t_u8 *)&roam_offload_cfg->bgscan_cfg,
sizeof(mlan_ds_misc_bgscan_cfg));
}
if (roam_offload_cfg->ees_param_set_flag) {
roam->ees_param_set_flag =
roam_offload_cfg->ees_param_set_flag;
memcpy((t_u8 *)&roam->ees_cfg,
(t_u8 *)&roam_offload_cfg->ees_cfg,
sizeof(mlan_ds_misc_ees_cfg));
}
roam->bcn_miss_threshold = roam_offload_cfg->bcn_miss_threshold;
roam->pre_bcn_miss_threshold =
roam_offload_cfg->pre_bcn_miss_threshold;
roam->repeat_count = roam_offload_cfg->repeat_count;
}
if (roam->config_mode == ROAM_OFFLOAD_SUSPEND_CFG) {
memcpy(roam->bssid_reconnect,
priv->phandle->auto_reconnect_bssid,
MLAN_MAC_ADDR_LENGTH);
roam->ssid_list.ssid_num = 1;
memcpy((t_u8 *)&roam->ssid_list.ssids[0].ssid,
(t_u8 *)&priv->phandle->auto_reconnect_ssid.ssid,
priv->phandle->auto_reconnect_ssid.ssid_len);
roam->retry_count = priv->phandle->auto_reconnect_retry_count;
}
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
/**
* @brief Request user scan
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param scan_cfg A pointer to wlan_bgscan_cfg structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_request_bgscan(moal_private *priv,
t_u8 wait_option, wlan_bgscan_cfg *scan_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_scan *scan = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
/* Allocate an IOCTL request buffer */
ioctl_req =
woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) +
sizeof(wlan_bgscan_cfg));
if (ioctl_req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)ioctl_req->pbuf;
scan->sub_command = MLAN_OID_SCAN_BGSCAN_CONFIG;
ioctl_req->req_id = MLAN_IOCTL_SCAN;
ioctl_req->action = MLAN_ACT_SET;
memcpy(scan->param.user_scan.scan_cfg_buf, scan_cfg,
sizeof(wlan_bgscan_cfg));
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, ioctl_req, wait_option);
if (status == MLAN_STATUS_FAILURE) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
/**
* @brief set bgscan config
*
* @param priv A pointer to moal_private structure
* @param buf A pointer to scan command buf
* @param length buf length
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_bg_scan(moal_private *priv, char *buf, int length)
{
t_u8 *ptr = buf + strlen("BGSCAN-CONFIG") + 1;
int buf_left = length - (strlen("BGSCAN-CONFIG") + 1);
int band = 0;
int num_ssid = 0;
int ssid_len = 0;
mlan_status ret = MLAN_STATUS_FAILURE;
ENTER();
memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg));
priv->scan_cfg.report_condition =
BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE;
while (buf_left >= 2) {
switch (*ptr) {
case WEXT_CSCAN_SSID_SECTION:
ssid_len = *(ptr + 1);
if ((buf_left < (ssid_len + 2)) ||
(ssid_len > MLAN_MAX_SSID_LENGTH)) {
PRINTM(MERROR,
"Invalid ssid, buf_left=%d, ssid_len=%d\n",
buf_left, ssid_len);
buf_left = 0;
break;
}
if (ssid_len &&
(num_ssid < (MRVDRV_MAX_SSID_LIST_LENGTH - 1))) {
strncpy(priv->scan_cfg.ssid_list[num_ssid].ssid,
ptr + 2, ssid_len);
priv->scan_cfg.ssid_list[num_ssid].max_len = 0;
PRINTM(MIOCTL, "BG scan: ssid=%s\n",
priv->scan_cfg.ssid_list[num_ssid].ssid);
num_ssid++;
}
buf_left -= ssid_len + 2;
ptr += ssid_len + 2;
break;
case WEXT_BGSCAN_RSSI_SECTION:
priv->scan_cfg.report_condition =
BG_SCAN_SSID_RSSI_MATCH |
BG_SCAN_WAIT_ALL_CHAN_DONE;
priv->scan_cfg.rssi_threshold = ptr[1];
PRINTM(MIOCTL, "BG scan: rssi_threshold=%d\n",
(int)priv->scan_cfg.rssi_threshold);
ptr += 2;
buf_left -= 2;
break;
case WEXT_BGSCAN_REPEAT_SECTION:
priv->scan_cfg.repeat_count = (t_u16)ptr[1];
PRINTM(MIOCTL, "BG scan: repeat_count=%d\n",
(int)priv->scan_cfg.repeat_count);
ptr += 2;
buf_left -= 2;
break;
case WEXT_BGSCAN_INTERVAL_SECTION:
if (buf_left < 3) {
PRINTM(MERROR,
"Invalid scan_interval, buf_left=%d\n",
buf_left);
buf_left = 0;
break;
}
priv->scan_cfg.scan_interval =
(ptr[2] << 8 | ptr[1]) * 1000;
PRINTM(MIOCTL, "BG scan: scan_interval=%d\n",
(int)priv->scan_cfg.scan_interval);
ptr += 3;
buf_left -= 3;
break;
default:
buf_left = 0;
break;
}
}
/** set bgscan when ssid_num > 0 */
if (num_ssid) {
if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
switch (band) {
case WIFI_FREQUENCY_BAND_2GHZ:
priv->scan_cfg.chan_list[0].radio_type =
0 | BAND_SPECIFIED;
break;
case WIFI_FREQUENCY_BAND_5GHZ:
priv->scan_cfg.chan_list[0].radio_type =
1 | BAND_SPECIFIED;
break;
default:
break;
}
priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA;
priv->scan_cfg.action = BG_SCAN_ACT_SET;
priv->scan_cfg.enable = MTRUE;
memcpy(priv->scan_cfg.random_mac, priv->random_mac, ETH_ALEN);
ret = woal_request_bgscan(priv, MOAL_IOCTL_WAIT,
&priv->scan_cfg);
}
done:
LEAVE();
return ret;
}
/**
* @brief stop bg scan
*
* @param priv A pointer to moal_private structure
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_stop_bg_scan(moal_private *priv, t_u8 wait_option)
{
wlan_bgscan_cfg scan_cfg;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
memset(&scan_cfg, 0, sizeof(scan_cfg));
scan_cfg.action = BG_SCAN_ACT_SET;
scan_cfg.enable = MFALSE;
ret = woal_request_bgscan(priv, wait_option, &scan_cfg);
LEAVE();
return ret;
}
/**
* @brief set bgscan config
*
* @param handle A pointer to moal_handle structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
void
woal_reconfig_bgscan(moal_handle *handle)
{
int i;
for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
if (handle->priv[i] &&
(GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) {
if (handle->priv[i]->bg_scan_start &&
handle->priv[i]->bg_scan_reported) {
PRINTM(MIOCTL, "Reconfig BGSCAN\n");
woal_request_bgscan(handle->priv[i],
MOAL_NO_WAIT,
&handle->priv[i]->scan_cfg);
handle->priv[i]->bg_scan_reported = MFALSE;
}
}
}
}
/**
* @brief set rssi low threshold
*
* @param priv A pointer to moal_private structure
* @param rssi A pointer to low rssi
* @param wait_option Wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_rssi_low_threshold(moal_private *priv, char *rssi, t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
int low_rssi = 0;
ENTER();
if (priv->media_connected == MFALSE)
goto done;
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_MISC_CFG;
misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT;
req->action = MLAN_ACT_SET;
misc->param.subscribe_event.evt_action = SUBSCRIBE_EVT_ACT_BITWISE_SET;
misc->param.subscribe_event.evt_bitmap = SUBSCRIBE_EVT_RSSI_LOW;
misc->param.subscribe_event.evt_bitmap |= SUBSCRIBE_EVT_PRE_BEACON_LOST;
misc->param.subscribe_event.pre_beacon_miss = DEFAULT_PRE_BEACON_MISS;
if (MLAN_STATUS_SUCCESS != woal_atoi(&low_rssi, rssi)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
#ifdef STA_CFG80211
priv->mrvl_rssi_low = low_rssi;
#endif
misc->param.subscribe_event.low_rssi = low_rssi;
misc->param.subscribe_event.low_rssi_freq = 0;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret == MLAN_STATUS_FAILURE) {
PRINTM(MERROR, "request set rssi_low_threshold fail!\n");
goto done;
}
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#if defined(STA_CFG80211)
/**
* @brief set rssi low threshold
*
* @param priv A pointer to moal_private structure
* @param event_id event id.
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_rssi_threshold(moal_private *priv, t_u32 event_id, t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
ENTER();
if (priv->media_connected == MFALSE)
goto done;
if (priv->mrvl_rssi_low)
goto done;
if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_LOW) {
if (priv->last_rssi_low < 100)
priv->last_rssi_low += priv->cqm_rssi_hyst;
priv->last_rssi_high = abs(priv->cqm_rssi_high_thold);
} else if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_HIGH) {
priv->last_rssi_low = abs(priv->cqm_rssi_thold);
if (priv->last_rssi_high > priv->cqm_rssi_hyst)
priv->last_rssi_high -= priv->cqm_rssi_hyst;
} else {
priv->last_rssi_low = abs(priv->cqm_rssi_thold);
priv->last_rssi_high = abs(priv->cqm_rssi_high_thold);
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_MISC_CFG;
misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT;
req->action = MLAN_ACT_SET;
if (!event_id && !priv->cqm_rssi_thold && !priv->cqm_rssi_hyst)
misc->param.subscribe_event.evt_action =
SUBSCRIBE_EVT_ACT_BITWISE_CLR;
else
misc->param.subscribe_event.evt_action =
SUBSCRIBE_EVT_ACT_BITWISE_SET;
misc->param.subscribe_event.evt_bitmap =
SUBSCRIBE_EVT_RSSI_LOW | SUBSCRIBE_EVT_RSSI_HIGH;
misc->param.subscribe_event.low_rssi_freq = 0;
misc->param.subscribe_event.low_rssi = priv->last_rssi_low;
misc->param.subscribe_event.high_rssi_freq = 0;
misc->param.subscribe_event.high_rssi = priv->last_rssi_high;
PRINTM(MIOCTL, "rssi_low=%d, rssi_high=%d action=%d\n",
(int)priv->last_rssi_low, (int)priv->last_rssi_high,
misc->param.subscribe_event.evt_action);
ret = woal_request_ioctl(priv, req, wait_option);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
/**
* @brief Get power mode
*
* @param priv A pointer to moal_private structure
* @param powermode A pointer to powermode buf
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_powermode(moal_private *priv, int *powermode)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
int ps_mode;
ENTER();
if (MLAN_STATUS_SUCCESS !=
woal_set_get_power_mgmt(priv, MLAN_ACT_GET, &ps_mode, 0,
MOAL_IOCTL_WAIT)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (ps_mode)
*powermode = MFALSE;
else
*powermode = MTRUE;
done:
LEAVE();
return ret;
}
/**
* @brief set scan type
*
* @param priv A pointer to moal_private structure
* @param scan_type MLAN_SCAN_TYPE_ACTIVE/MLAN_SCAN_TYPE_PASSIVE
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_scan_type(moal_private *priv, t_u32 scan_type)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_scan *scan = NULL;
mlan_ioctl_req *req = NULL;
mlan_scan_cfg scan_cfg;
ENTER();
memset(&scan_cfg, 0, sizeof(scan_cfg));
if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)req->pbuf;
scan->sub_command = MLAN_OID_SCAN_CONFIG;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_SET;
memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg));
scan_cfg.scan_type = scan_type;
PRINTM(MIOCTL, "Set scan_type=%d\n", (int)scan_type);
memcpy(&scan->param.scan_cfg, &scan_cfg, sizeof(mlan_scan_cfg));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief enable/disable ext_scan
*
* @param priv A pointer to moal_private structure
* @param enable MTRUE -- enable, MFALSE --disable
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_enable_ext_scan(moal_private *priv, t_u8 enable)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_scan *scan = NULL;
mlan_ioctl_req *req = NULL;
mlan_scan_cfg scan_cfg;
ENTER();
memset(&scan_cfg, 0, sizeof(scan_cfg));
if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
scan = (mlan_ds_scan *)req->pbuf;
scan->sub_command = MLAN_OID_SCAN_CONFIG;
req->req_id = MLAN_IOCTL_SCAN;
req->action = MLAN_ACT_SET;
memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg));
scan_cfg.ext_scan = enable;
PRINTM(MIOCTL, "Set ext_scan=%d\n", (int)enable);
memcpy(&scan->param.scan_cfg, &scan_cfg, sizeof(mlan_scan_cfg));
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief set power mode
*
* @param priv A pointer to moal_private structure
* @param powermode A pointer to powermode string.
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_powermode(moal_private *priv, char *powermode)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
int disabled;
ENTER();
if (*powermode == '1') {
PRINTM(MIOCTL, "Disable power save\n");
disabled = 1;
} else if (*powermode == '0') {
PRINTM(MIOCTL, "Enable power save\n");
disabled = 0;
} else {
PRINTM(MERROR, "unsupported power mode\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (MLAN_STATUS_SUCCESS !=
woal_set_get_power_mgmt(priv, MLAN_ACT_SET, &disabled, 0,
MOAL_IOCTL_WAIT))
ret = MLAN_STATUS_FAILURE;
done:
LEAVE();
return ret;
}
/**
* @brief set combo scan
*
* @param priv A pointer to moal_private structure
* @param buf A pointer to scan command buf
* @param length buf length
*
* @return 0 -- success, otherwise fail
*/
int
woal_set_combo_scan(moal_private *priv, char *buf, int length)
{
int ret = 0;
wlan_user_scan_cfg scan_cfg;
t_u8 *ptr = buf + WEXT_CSCAN_HEADER_SIZE;
int buf_left = length - WEXT_CSCAN_HEADER_SIZE;
int num_ssid = 0;
int num_chan = 0;
int ssid_len = 0;
int i = 0;
t_u16 passive_scan_time = 0;
t_u16 specific_scan_time = 0;
ENTER();
memset(&scan_cfg, 0, sizeof(scan_cfg));
while (buf_left >= 2) {
switch (*ptr) {
case WEXT_CSCAN_SSID_SECTION:
ssid_len = *(ptr + 1);
if ((buf_left < (ssid_len + 2)) ||
(ssid_len > MLAN_MAX_SSID_LENGTH)) {
PRINTM(MERROR,
"Invalid ssid, buf_left=%d, ssid_len=%d\n",
buf_left, ssid_len);
buf_left = 0;
break;
}
if (ssid_len &&
(num_ssid < (MRVDRV_MAX_SSID_LIST_LENGTH - 1))) {
strncpy(scan_cfg.ssid_list[num_ssid].ssid,
ptr + 2, ssid_len);
scan_cfg.ssid_list[num_ssid].max_len = 0;
PRINTM(MIOCTL, "Combo scan: ssid=%s\n",
scan_cfg.ssid_list[num_ssid].ssid);
num_ssid++;
}
buf_left -= ssid_len + 2;
ptr += ssid_len + 2;
break;
case WEXT_CSCAN_CHANNEL_SECTION:
num_chan = ptr[1];
if ((buf_left < (num_chan + 2)) ||
(num_chan > WLAN_USER_SCAN_CHAN_MAX)) {
PRINTM(MERROR,
"Invalid channel list, buf_left=%d, num_chan=%d\n",
buf_left, num_chan);
buf_left = 0;
break;
}
for (i = 0; i < num_chan; i++) {
scan_cfg.chan_list[i].chan_number = ptr[2 + i];
PRINTM(MIOCTL, "Combo scan: chan=%d\n",
scan_cfg.chan_list[i].chan_number);
}
buf_left -= 2 + num_chan;
ptr += 2 + num_chan;
break;
case WEXT_CSCAN_PASV_DWELL_SECTION:
if (buf_left < 3) {
PRINTM(MERROR,
"Invalid PASV_DWELL_SECTION, buf_left=%d\n",
buf_left);
buf_left = 0;
break;
}
passive_scan_time = ptr[2] << 8 | ptr[1];
ptr += 3;
buf_left -= 3;
break;
case WEXT_CSCAN_HOME_DWELL_SECTION:
if (buf_left < 3) {
PRINTM(MERROR,
"Invalid HOME_DWELL_SECTION, buf_left=%d\n",
buf_left);
buf_left = 0;
break;
}
specific_scan_time = ptr[2] << 8 | ptr[1];
ptr += 3;
buf_left -= 3;
break;
default:
buf_left = 0;
break;
}
}
if (passive_scan_time || specific_scan_time) {
PRINTM(MIOCTL,
"Set passive_scan_time=%d specific_scan_time=%d\n",
passive_scan_time, specific_scan_time);
if (MLAN_STATUS_FAILURE ==
woal_set_scan_time(priv, 0, passive_scan_time,
specific_scan_time)) {
ret = -EFAULT;
goto done;
}
}
if (num_ssid || num_chan) {
if (num_ssid) {
/* Add broadcast scan to ssid_list */
scan_cfg.ssid_list[num_ssid].max_len = 0xff;
if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)
woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE);
}
if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg))
ret = -EFAULT;
if (num_ssid && (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE))
woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE);
} else {
/* request broadcast scan */
if (MLAN_STATUS_FAILURE == woal_do_scan(priv, NULL))
ret = -EFAULT;
}
done:
LEAVE();
return ret;
}
/**
* @brief Get band
*
* @param priv A pointer to moal_private structure
* @param band A pointer to band buf
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_get_band(moal_private *priv, int *band)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_radio_cfg *radio_cfg = NULL;
int support_band = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
radio_cfg->sub_command = MLAN_OID_BAND_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
/* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN */
req->action = MLAN_ACT_GET;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (radio_cfg->param.band_cfg.
config_bands & (BAND_B | BAND_G | BAND_GN))
support_band |= WIFI_FREQUENCY_BAND_2GHZ;
if (radio_cfg->param.band_cfg.config_bands & (BAND_A | BAND_AN))
support_band |= WIFI_FREQUENCY_BAND_5GHZ;
*band = support_band;
if (support_band == WIFI_FREQUENCY_ALL_BAND)
*band = WIFI_FREQUENCY_BAND_AUTO;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief set band
*
* @param priv A pointer to moal_private structure
* @param pband A pointer to band string.
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_band(moal_private *priv, char *pband)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
int band = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_radio_cfg *radio_cfg = NULL;
ENTER();
if (priv->media_connected == MTRUE) {
PRINTM(MERROR, "Set band is not allowed in connected state\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
radio_cfg->sub_command = MLAN_OID_BAND_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
/* Get fw supported values from MLAN */
req->action = MLAN_ACT_GET;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (*pband == '0') {
PRINTM(MIOCTL, "Set band to AUTO\n");
band = radio_cfg->param.band_cfg.fw_bands;
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev &&
priv->wdev->wiphy) {
if (radio_cfg->param.band_cfg.fw_bands & BAND_A)
priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] =
&cfg80211_band_5ghz;
priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] =
&cfg80211_band_2ghz;
}
#endif
} else if (*pband == '1') {
PRINTM(MIOCTL, "Set band to 5G\n");
if (!(radio_cfg->param.band_cfg.fw_bands & BAND_A)) {
PRINTM(MERROR, "Don't support 5G band\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
band = BAND_A;
if (radio_cfg->param.band_cfg.fw_bands & BAND_AN)
band |= BAND_AN;
if (radio_cfg->param.band_cfg.fw_bands & BAND_AAC)
band |= BAND_AAC;
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev &&
priv->wdev->wiphy) {
priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] =
&cfg80211_band_5ghz;
priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
}
#endif
} else if (*pband == '2') {
PRINTM(MIOCTL, "Set band to 2G\n");
band = BAND_B | BAND_G;
band |= BAND_GN;
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev &&
priv->wdev->wiphy) {
priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] =
&cfg80211_band_2ghz;
}
#endif
} else {
PRINTM(MERROR, "unsupported band\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Set config_bands to MLAN */
req->action = MLAN_ACT_SET;
memset(&radio_cfg->param.band_cfg, 0, sizeof(mlan_ds_band_cfg));
radio_cfg->param.band_cfg.config_bands = band;
radio_cfg->param.band_cfg.adhoc_start_band = band;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Add RX Filter
*
* @param priv A pointer to moal_private structure
* @param rxfilter A pointer to rxfilter string.
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_add_rxfilter(moal_private *priv, char *rxfilter)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* Android command:
"DRIVER RXFILTER-ADD 0"
"DRIVER RXFILTER-ADD 1"
"DRIVER RXFILTER-ADD 3" */
if (*rxfilter == '0') {
PRINTM(MIOCTL, "Add IPV4 multicast filter\n");
priv->rx_filter |= RX_FILTER_IPV4_MULTICAST;
} else if (*rxfilter == '1') {
PRINTM(MIOCTL, "Add broadcast filter\n");
priv->rx_filter |= RX_FILTER_BROADCAST;
} else if (*rxfilter == '2') {
PRINTM(MIOCTL, "Add unicast filter\n");
priv->rx_filter |= RX_FILTER_UNICAST;
} else if (*rxfilter == '3') {
PRINTM(MIOCTL, "Add IPV6 multicast fitler\n");
priv->rx_filter |= RX_FILTER_IPV6_MULTICAST;
} else {
PRINTM(MERROR, "unsupported rx fitler\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
done:
LEAVE();
return ret;
}
/**
* @brief Remove RX Filter
*
* @param priv A pointer to moal_private structure
* @param rxfilter A pointer to rxfilter string.
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_remove_rxfilter(moal_private *priv, char *rxfilter)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
if (*rxfilter == '0') {
PRINTM(MIOCTL, "Remove IPV4 multicast filter\n");
priv->rx_filter &= ~RX_FILTER_IPV4_MULTICAST;
} else if (*rxfilter == '1') {
PRINTM(MIOCTL, "Remove broadcast filter\n");
priv->rx_filter &= ~RX_FILTER_BROADCAST;
} else if (*rxfilter == '2') {
PRINTM(MIOCTL, "Remove unicast filter\n");
priv->rx_filter &= ~RX_FILTER_UNICAST;
} else if (*rxfilter == '3') {
PRINTM(MIOCTL, "Remove IPV6 multicast fitler\n");
priv->rx_filter &= ~RX_FILTER_IPV6_MULTICAST;
} else {
PRINTM(MERROR, "unsupported rx fitler\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
done:
LEAVE();
return ret;
}
/**
* @brief Set/Get WMM IE QoS configuration
*
* @param priv A pointer to moal_private structure
* @param action Action set or get
* @param qos_cfg A pointer to QoS configuration structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_priv_qos_cfg(moal_private *priv, t_u32 action, char *qos_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_wmm_cfg *cfg = NULL;
mlan_ioctl_req *req = NULL;
int qosinfo = 0;
ENTER();
if (qos_cfg == NULL) {
PRINTM(MERROR, "QOS info buffer is null\n");
return MLAN_STATUS_FAILURE;
}
if ((action == MLAN_ACT_SET) &&
(MLAN_STATUS_SUCCESS != woal_atoi(&qosinfo, qos_cfg))) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
cfg = (mlan_ds_wmm_cfg *)req->pbuf;
cfg->sub_command = MLAN_OID_WMM_CFG_QOS;
req->req_id = MLAN_IOCTL_WMM_CFG;
req->action = action;
if (action == MLAN_ACT_SET) {
cfg->param.qos_cfg = (t_u8)qosinfo;
PRINTM(MIOCTL, "set qosinfo=%d\n", qosinfo);
}
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (action == MLAN_ACT_GET)
*qos_cfg = cfg->param.qos_cfg;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set sleep period
*
* @param priv A pointer to moal_private structure
* @param psleeppd A pointer to sleep period configuration structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_sleeppd(moal_private *priv, char *psleeppd)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_pm_cfg *pm_cfg = NULL;
mlan_ioctl_req *req = NULL;
int sleeppd = 0;
ENTER();
if (MLAN_STATUS_SUCCESS != woal_atoi(&sleeppd, psleeppd)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
PRINTM(MIOCTL, "set sleeppd=%d\n", sleeppd);
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
pm_cfg = (mlan_ds_pm_cfg *)req->pbuf;
pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD;
req->req_id = MLAN_IOCTL_PM_CFG;
if ((sleeppd <= MAX_SLEEP_PERIOD && sleeppd >= MIN_SLEEP_PERIOD) ||
(sleeppd == 0)
|| (sleeppd == SLEEP_PERIOD_RESERVED_FF)
) {
req->action = MLAN_ACT_SET;
pm_cfg->param.sleep_period = sleeppd;
} else {
ret = MLAN_STATUS_FAILURE;
goto done;
}
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set scan period function
*
* @param priv A pointer to moal_private structure
* @param buf A pointer to scan command buf
* @param length buf length
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
int
woal_set_scan_cfg(moal_private *priv, char *buf, int length)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u8 *ptr = buf + NL80211_SCANCFG_HEADER_SIZE;
int buf_left = length - NL80211_SCANCFG_HEADER_SIZE;
t_u16 active_scan_time = 0;
t_u16 passive_scan_time = 0;
t_u16 specific_scan_time = 0;
ENTER();
while (buf_left >= 2) {
switch (*ptr) {
case NL80211_SCANCFG_ACTV_DWELL_SECTION:
if (buf_left < 3) {
PRINTM(MERROR,
"Invalid ACTV_DWELL_SECTION, buf_left=%d\n",
buf_left);
buf_left = 0;
break;
}
active_scan_time = ptr[2] << 8 | ptr[1];
ptr += 3;
buf_left -= 3;
break;
case NL80211_SCANCFG_PASV_DWELL_SECTION:
if (buf_left < 3) {
PRINTM(MERROR,
"Invalid PASV_DWELL_SECTION, buf_left=%d\n",
buf_left);
buf_left = 0;
break;
}
passive_scan_time = ptr[2] << 8 | ptr[1];
ptr += 3;
buf_left -= 3;
break;
case NL80211_SCANCFG_SPCF_DWELL_SECTION:
if (buf_left < 3) {
PRINTM(MERROR,
"Invalid SPCF_DWELL_SECTION, buf_left=%d\n",
buf_left);
buf_left = 0;
break;
}
specific_scan_time = ptr[2] << 8 | ptr[1];
ptr += 3;
buf_left -= 3;
break;
default:
buf_left = 0;
break;
}
}
if (active_scan_time || passive_scan_time || specific_scan_time) {
PRINTM(MIOCTL,
"Set active_scan_time= %d passive_scan_time=%d specific_scan_time=%d\n",
active_scan_time, passive_scan_time, specific_scan_time);
if (MLAN_STATUS_FAILURE ==
woal_set_scan_time(priv, active_scan_time,
passive_scan_time, specific_scan_time)) {
ret = -EFAULT;
}
}
LEAVE();
return ret;
}
/**
* @brief Set Radio On/OFF
*
* @param priv A pointer to moal_private structure
* @param option Radio Option
*
* @return 0 --success, otherwise fail
*/
int
woal_set_radio(moal_private *priv, t_u8 option)
{
int ret = 0;
mlan_ds_radio_cfg *radio = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if ((option != 0) && (option != 1)) {
ret = -EINVAL;
goto done;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
radio = (mlan_ds_radio_cfg *)req->pbuf;
radio->sub_command = MLAN_OID_RADIO_CTRL;
req->req_id = MLAN_IOCTL_RADIO_CFG;
req->action = MLAN_ACT_SET;
radio->param.radio_on_off = option;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS)
ret = -EFAULT;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif /* STA_SUPPORT */
/**
* @brief Set/Get configure multi-channel policy
*
* @param priv A pointer to moal_private structure
* @param enable A pointer to enable
* @param wait_option wait_option of ioctl
* @param action action of ioctl
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_mc_policy_cfg(moal_private *priv, t_u16 *enable,
t_u8 wait_option, t_u8 action)
{
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *cfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
status = MLAN_STATUS_FAILURE;
goto done;
}
cfg = (mlan_ds_misc_cfg *)req->pbuf;
cfg->sub_command = MLAN_OID_MISC_MULTI_CHAN_POLICY;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = action;
if (MLAN_ACT_SET == action)
cfg->param.multi_chan_policy = *enable;
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS)
goto done;
if (MLAN_ACT_GET == action)
*enable = cfg->param.multi_chan_policy;
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
/**
* @brief Set/Get network monitor configurations
*
* @param priv Pointer to moal_private structure
* @param wait_option wait option
* @param enable Enable/Disable
* @param filter Filter flag - Management/Control/Data
* @param band_chan_cfg Network monitor band channel config
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
mlan_status
woal_set_net_monitor(moal_private *priv, t_u8 wait_option,
t_u8 enable, t_u8 filter,
netmon_band_chan_cfg * band_chan_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_net_monitor *net_mon = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
net_mon = (mlan_ds_misc_net_monitor *)&misc->param.net_mon;
misc->sub_command = MLAN_OID_MISC_NET_MONITOR;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
net_mon->enable_net_mon = enable;
if (net_mon->enable_net_mon) {
net_mon->filter_flag = filter;
if (band_chan_cfg && band_chan_cfg->channel) {
/* fill TLV only if chan band is specified */
net_mon->band = band_chan_cfg->band;
net_mon->channel = band_chan_cfg->channel;
net_mon->chan_bandwidth = band_chan_cfg->chan_bandwidth;
}
}
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* update chan band values from response in band_chan_cfg */
if (net_mon->enable_net_mon) {
if (band_chan_cfg) {
band_chan_cfg->band = net_mon->band;
band_chan_cfg->channel = net_mon->channel;
band_chan_cfg->chan_bandwidth = net_mon->chan_bandwidth;
}
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
#endif
/**
* @brief Send delelte all BA command to MLAN
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_delba_all(moal_private *priv, t_u8 wait_option)
{
mlan_ioctl_req *req = NULL;
mlan_ds_11n_cfg *cfg_11n = NULL;
mlan_ds_11n_delba *del_ba = NULL;
t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = { 0 };
mlan_status status = MLAN_STATUS_SUCCESS;
int ret = 0;
req = (mlan_ioctl_req *)
woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
cfg_11n = (mlan_ds_11n_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_11N_CFG;
cfg_11n->sub_command = MLAN_OID_11N_CFG_DELBA;
del_ba = &cfg_11n->param.del_ba;
memset(del_ba, 0, sizeof(mlan_ds_11n_delba));
del_ba->direction = DELBA_RX | DELBA_TX;
del_ba->tid = DELBA_ALL_TIDS;
memcpy(del_ba->peer_mac_addr, zero_mac, ETH_ALEN);
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief config RTT to mlan layer
*
* @param priv A pointer to moal_private structure
* @param ch_info A pointer to wifi_channel_info
* @param bandcfg A pointer to Band_Config_t
*
* @return void
*/
void
woal_channel_info_to_bandcfg(moal_private *priv, wifi_channel_info * ch_info,
Band_Config_t *bandcfg)
{
t_u8 channel = 0;
if (!ch_info || !bandcfg)
return;
channel = ieee80211_frequency_to_channel(ch_info->center_freq);
switch (ch_info->width) {
case WIFI_CHAN_WIDTH_10:
bandcfg->chanWidth = CHAN_BW_10MHZ;
break;
case WIFI_CHAN_WIDTH_20:
bandcfg->chanWidth = CHAN_BW_20MHZ;
break;
case WIFI_CHAN_WIDTH_40:
bandcfg->chanWidth = CHAN_BW_40MHZ;
break;
case WIFI_CHAN_WIDTH_80:
bandcfg->chanWidth = CHAN_BW_80MHZ;
break;
default:
bandcfg->chanWidth = CHAN_BW_20MHZ;
break;
}
bandcfg->chan2Offset = SEC_CHAN_NONE;
if (bandcfg->chanWidth == CHAN_BW_40MHZ) {
if (ch_info->center_freq0 < ch_info->center_freq)
bandcfg->chan2Offset = SEC_CHAN_BELOW;
else
bandcfg->chan2Offset = SEC_CHAN_ABOVE;
} else if (bandcfg->chanWidth == CHAN_BW_80MHZ)
bandcfg->chan2Offset = woal_get_second_channel_offset(channel);
bandcfg->chanBand = (channel <= MAX_BG_CHANNEL) ? BAND_2GHZ : BAND_5GHZ;
bandcfg->scanMode = SCAN_MODE_MANUAL;
return;
}
/**
* @brief config RTT to mlan layer
*
* @param priv A pointer to moal_private structure
* @param ch_info A pointer to wifi_channel_info
* @param bandcfg A pointer to Band_Config_t
*
* @return void
*/
void
woal_bandcfg_to_channel_info(moal_private *priv, Band_Config_t *bandcfg,
t_u8 channel, wifi_channel_info * ch_info)
{
if (!ch_info || !bandcfg)
return;
ch_info->center_freq =
ieee80211_channel_to_frequency(channel,
(channel <=
MAX_BG_CHANNEL) ?
NL80211_BAND_2GHZ :
NL80211_BAND_5GHZ);
switch (bandcfg->chanWidth) {
case CHAN_BW_10MHZ:
ch_info->width = WIFI_CHAN_WIDTH_10;
break;
case CHAN_BW_20MHZ:
ch_info->width = WIFI_CHAN_WIDTH_20;
break;
case CHAN_BW_40MHZ:
ch_info->width = WIFI_CHAN_WIDTH_40;
break;
case CHAN_BW_80MHZ:
ch_info->width = WIFI_CHAN_WIDTH_80;
break;
default:
ch_info->width = WIFI_CHAN_WIDTH_20;
break;
}
return;
}
/**
* @brief config RTT to mlan layer
*
* @param priv A pointer to moal_private structure
* @param wait_option wait option
* @param hotspotcfg A pointer to rtt_config_params_t
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_config_rtt(moal_private *priv, t_u8 wait_option,
wifi_rtt_config_params_t * rtt_params_in)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_rtt_config_params *rtt_params = NULL;
t_u32 i = 0;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req->action = MLAN_ACT_SET;
req->req_id = MLAN_IOCTL_MISC_CFG;
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_CONFIG_RTT;
rtt_params = &(misc->param.rtt_params);
rtt_params->rtt_config_num = rtt_params_in->rtt_config_num;
for (i = 0; i < MIN(rtt_params->rtt_config_num, MAX_RTT_CONFIG_NUM);
i++) {
memcpy(rtt_params->rtt_config[i].addr,
rtt_params_in->rtt_config[i].addr,
sizeof(rtt_params->rtt_config[i].addr));
rtt_params->rtt_config[i].type =
rtt_params_in->rtt_config[i].type;
rtt_params->rtt_config[i].peer =
rtt_params_in->rtt_config[i].peer;
rtt_params->rtt_config[i].channel =
ieee80211_frequency_to_channel(rtt_params_in->
rtt_config[i].channel.
center_freq);
woal_channel_info_to_bandcfg(priv,
&(rtt_params_in->rtt_config[i].
channel),
&(rtt_params->rtt_config[i].
bandcfg));
rtt_params->rtt_config[i].burst_period =
rtt_params_in->rtt_config[i].burst_period;
rtt_params->rtt_config[i].num_burst =
rtt_params_in->rtt_config[i].num_burst;
rtt_params->rtt_config[i].num_frames_per_burst =
rtt_params_in->rtt_config[i].num_frames_per_burst;
rtt_params->rtt_config[i].num_retries_per_rtt_frame =
rtt_params_in->rtt_config[i].num_retries_per_rtt_frame;
rtt_params->rtt_config[i].num_retries_per_ftmr =
rtt_params_in->rtt_config[i].num_retries_per_ftmr;
rtt_params->rtt_config[i].LCI_request =
rtt_params_in->rtt_config[i].LCI_request;
rtt_params->rtt_config[i].LCR_request =
rtt_params_in->rtt_config[i].LCR_request;
rtt_params->rtt_config[i].burst_duration =
rtt_params_in->rtt_config[i].burst_duration;
rtt_params->rtt_config[i].preamble =
rtt_params_in->rtt_config[i].preamble;
rtt_params->rtt_config[i].bw = rtt_params_in->rtt_config[i].bw;
}
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief cancel RTT to mlan layer
*
* @param priv A pointer to moal_private structure
* @param wait_option wait option
* @param hotspotcfg A pointer to rtt_config_params_t
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_cancel_rtt(moal_private *priv, t_u8 wait_option, t_u32 addr_num,
t_u8 addr[][MLAN_MAC_ADDR_LENGTH])
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_rtt_cancel_params *rtt_cancel = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req->action = MLAN_ACT_SET;
req->req_id = MLAN_IOCTL_MISC_CFG;
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_CANCEL_RTT;
rtt_cancel = &(misc->param.rtt_cancel);
rtt_cancel->rtt_cancel_num = addr_num;
memcpy(rtt_cancel->rtt_cancel, addr,
sizeof(rtt_cancel->rtt_cancel[0]) *
MIN(rtt_cancel->rtt_cancel_num, MAX_RTT_CONFIG_NUM));
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief cancel RTT to mlan layer
*
* @param priv A pointer to moal_private structure
* @param wait_option wait option
* @param hotspotcfg A pointer to rtt_config_params_t
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_rtt_responder_cfg(moal_private *priv, t_u8 wait_option,
mlan_rtt_responder * rtt_rsp_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc = NULL;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
req->action = MLAN_ACT_SET;
req->req_id = MLAN_IOCTL_MISC_CFG;
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_RTT_RESPONDER_CFG;
memcpy(&(misc->param.rtt_rsp_cfg), rtt_rsp_cfg,
sizeof(misc->param.rtt_rsp_cfg));
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
memcpy(rtt_rsp_cfg, &(misc->param.rtt_rsp_cfg), sizeof(*rtt_rsp_cfg));
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Send 11d enable/disable command to firmware.
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param enable Enable/Disable 11d
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, otherwise fail
*/
mlan_status
woal_set_11d(moal_private *priv, t_u8 wait_option, t_u8 enable)
{
mlan_ioctl_req *req = NULL;
mlan_ds_snmp_mib *snmp = NULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib));
if (req == NULL) {
LEAVE();
return -ENOMEM;
}
req->action = MLAN_ACT_SET;
req->req_id = MLAN_IOCTL_SNMP_MIB;
snmp = (mlan_ds_snmp_mib *)req->pbuf;
snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D;
snmp->param.oid_value = enable;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Handle miscellaneous ioctls for asynchronous command response
*
* @param priv Pointer to moal_private structure
* @param info Pointer to mlan_ds_misc_cfg structure
*
* @return N/A
*/
void
woal_ioctl_get_misc_conf(moal_private *priv, mlan_ds_misc_cfg *info)
{
char event[1000];
mlan_ds_host_clock *host_clock = NULL;
ENTER();
switch (info->sub_command) {
case MLAN_OID_MISC_GET_CORRELATED_TIME:
host_clock = &info->param.host_clock;
memset(event, 0, sizeof(event));
sprintf(event, "%s %llu %llu", CUS_EVT_GET_CORRELATED_TIME,
host_clock->time, host_clock->fw_time);
woal_broadcast_event(priv, event, strlen(event));
PRINTM(MERROR, "CMD = %s\n", event);
default:
break;
}
}
module_param(disconnect_on_suspend, int, 0);
MODULE_PARM_DESC(disconnect_on_suspend,
"1: Enable disconnect wifi on suspend; 0: Disable disconnect wifi on suspend");