2025-05-10 21:58:58 +08:00

3752 lines
98 KiB
C

/*
* Copyright (C) 2015 Spreadtrum Communications Inc.
*
* Authors :
* Keguang Zhang <keguang.zhang@spreadtrum.com>
* Jingxiang Li <Jingxiang.li@spreadtrum.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "sprdwl.h"
#include "cmdevt.h"
#include "cfg80211.h"
#include "msg.h"
#include "txrx.h"
#include "intf_ops.h"
#include "vendor.h"
#include "work.h"
#ifdef NAN_SUPPORT
#include "nan.h"
#endif /* NAN_SUPPORT */
#include "tx_msg.h"
#include "rx_msg.h"
#include "wl_intf.h"
#ifdef DFS_MASTER
#include "11h.h"
#endif
#include "rf_marlin3.h"
#include <linux/kthread.h>
#ifdef WMMAC_WFA_CERTIFICATION
#include "qos.h"
#endif
#include <linux/completion.h>
struct sprdwl_cmd {
u8 cmd_id;
int init_ok;
u32 mstime;
void *data;
atomic_t refcnt;
/* spin lock for command */
spinlock_t lock;
/* mutex for command */
struct mutex cmd_lock;
/* wake_lock for command */
struct wakeup_source *wake_lock;
/*complettion for command*/
struct completion completed;
};
struct sprdwl_cmd g_sprdwl_cmd;
const uint16_t CRC_table[] = {
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00,
0x2800, 0xE401, 0xA001, 0x6C00, 0x7800, 0xB401,
0x5000, 0x9C01, 0x8801, 0x4400,
};
#define C2S(x) \
{ \
case x: \
str = #x;\
break; \
}
static int bss_count;
static const char *cmd2str(u8 cmd)
{
const char *str = NULL;
switch (cmd) {
C2S(WIFI_CMD_ERR)
C2S(WIFI_CMD_GET_INFO)
C2S(WIFI_CMD_SET_REGDOM)
C2S(WIFI_CMD_OPEN)
C2S(WIFI_CMD_CLOSE)
C2S(WIFI_CMD_POWER_SAVE)
C2S(WIFI_CMD_SET_PARAM)
C2S(WIFI_CMD_REQ_LTE_CONCUR)
C2S(WIFI_CMD_SYNC_VERSION)
C2S(WIFI_CMD_CONNECT)
C2S(WIFI_CMD_SCAN)
C2S(WIFI_CMD_SCHED_SCAN)
C2S(WIFI_CMD_DISCONNECT)
C2S(WIFI_CMD_KEY)
C2S(WIFI_CMD_SET_PMKSA)
C2S(WIFI_CMD_GET_STATION)
C2S(WIFI_CMD_SET_CHANNEL)
C2S(WIFI_CMD_START_AP)
C2S(WIFI_CMD_DEL_STATION)
C2S(WIFI_CMD_SET_BLACKLIST)
C2S(WIFI_CMD_SET_WHITELIST)
C2S(WIFI_CMD_MULTICAST_FILTER)
C2S(WIFI_CMD_TX_MGMT)
C2S(WIFI_CMD_REGISTER_FRAME)
C2S(WIFI_CMD_REMAIN_CHAN)
C2S(WIFI_CMD_CANCEL_REMAIN_CHAN)
C2S(WIFI_CMD_SET_IE)
C2S(WIFI_CMD_NOTIFY_IP_ACQUIRED)
C2S(WIFI_CMD_SET_CQM)
C2S(WIFI_CMD_SET_ROAM_OFFLOAD)
C2S(WIFI_CMD_SET_MEASUREMENT)
C2S(WIFI_CMD_SET_QOS_MAP)
C2S(WIFI_CMD_TDLS)
C2S(WIFI_CMD_11V)
C2S(WIFI_CMD_NPI_MSG)
C2S(WIFI_CMD_NPI_GET)
C2S(WIFI_CMD_ASSERT)
C2S(WIFI_CMD_FLUSH_SDIO)
C2S(WIFI_CMD_ADD_TX_TS)
C2S(WIFI_CMD_DEL_TX_TS)
C2S(WIFI_CMD_LLSTAT)
C2S(WIFI_CMD_GSCAN)
C2S(WIFI_CMD_RSSI_MONITOR)
C2S(WIFI_CMD_IBSS_JOIN)
C2S(WIFI_CMD_SET_IBSS_ATTR)
C2S(WIFI_CMD_NAN)
C2S(WIFI_CMD_RND_MAC)
C2S(WIFI_CMD_BA)
C2S(WIFI_CMD_SET_MAX_CLIENTS_ALLOWED)
C2S(WIFI_CMD_TX_DATA)
C2S(WIFI_CMD_ADDBA_REQ)
C2S(WIFI_CMD_DELBA_REQ)
C2S(WIFI_CMD_SET_PROTECT_MODE)
C2S(WIFI_CMD_GET_PROTECT_MODE)
C2S(WIFI_CMD_DOWNLOAD_INI)
C2S(WIFI_CMD_PACKET_OFFLOAD)
#ifdef DFS_MASTER
C2S(WIFI_CMD_RADAR_DETECT)
C2S(WIFI_CMD_RESET_BEACON)
#endif
C2S(WIFI_CMD_VOWIFI_DATA_PROTECT)
C2S(WIFI_CMD_SET_TLV)
C2S(WIFI_CMD_SET_WOWLAN)
default :
return "WIFI_CMD_UNKNOWN";
}
return str;
}
#undef C2S
#define AR2S(x) \
{ \
case x: \
str = #x; \
break; \
}
static const char *assert_reason_to_str(u8 reason)
{
const char *str = NULL;
switch (reason) {
AR2S(SCAN_ERROR)
AR2S(RSP_CNT_ERROR)
AR2S(HANDLE_FLAG_ERROR)
AR2S(CMD_RSP_TIMEOUT_ERROR)
AR2S(LOAD_INI_DATA_FAILED)
AR2S(DOWNLOAD_INI_DATA_FAILED)
default :
return "UNKNOWN ASSERT REASON";
}
return str;
}
#undef AR2S
uint16_t CRC16(uint8_t *buf, uint16_t len)
{
uint16_t CRC = 0xFFFF;
uint16_t i;
uint8_t ch_char;
for (i = 0; i < len; i++) {
ch_char = *buf++;
CRC = CRC_table[(ch_char ^ CRC) & 15] ^ (CRC >> 4);
CRC = CRC_table[((ch_char >> 4) ^ CRC) & 15] ^ (CRC >> 4);
}
return CRC;
}
static const char *err2str(s8 error)
{
char *str = NULL;
switch (error) {
case SPRDWL_CMD_STATUS_ARG_ERROR:
str = "SPRDWL_CMD_STATUS_ARG_ERROR";
break;
case SPRDWL_CMD_STATUS_GET_RESULT_ERROR:
str = "SPRDWL_CMD_STATUS_GET_RESULT_ERROR";
break;
case SPRDWL_CMD_STATUS_EXEC_ERROR:
str = "SPRDWL_CMD_STATUS_EXEC_ERROR";
break;
case SPRDWL_CMD_STATUS_MALLOC_ERROR:
str = "SPRDWL_CMD_STATUS_MALLOC_ERROR";
break;
case SPRDWL_CMD_STATUS_WIFIMODE_ERROR:
str = "SPRDWL_CMD_STATUS_WIFIMODE_ERROR";
break;
case SPRDWL_CMD_STATUS_ERROR:
str = "SPRDWL_CMD_STATUS_ERROR";
break;
case SPRDWL_CMD_STATUS_CONNOT_EXEC_ERROR:
str = "SPRDWL_CMD_STATUS_CONNOT_EXEC_ERROR";
break;
case SPRDWL_CMD_STATUS_NOT_SUPPORT_ERROR:
str = "SPRDWL_CMD_STATUS_NOT_SUPPORT_ERROR";
break;
case SPRDWL_CMD_STATUS_CRC_ERROR:
str = "SPRDWL_CMD_STATUS_CRC_ERROR";
break;
case SPRDWL_CMD_STATUS_INI_INDEX_ERROR:
str = "SPRDWL_CMD_STATUS_INI_INDEX_ERROR";
break;
case SPRDWL_CMD_STATUS_LENGTH_ERROR:
str = "SPRDWL_CMD_STATUS_LENGTH_ERROR";
break;
case SPRDWL_CMD_STATUS_OTHER_ERROR:
str = "SPRDWL_CMD_STATUS_OTHER_ERROR";
break;
case SPRDWL_CMD_STATUS_OK:
str = "CMD STATUS OK";
break;
default:
str = "SPRDWL_CMD_STATUS_UNKNOWN_ERROR";
break;
}
return str;
}
#define SPRDWL_CMD_EXIT_VAL 0x8000
int sprdwl_cmd_init(void)
{
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
/* memset(cmd, 0, sizeof(*cmd)); */
cmd->data = NULL;
#ifdef CONFIG_WIFI_RK_PM_PRIVATE_API
cmd->wake_lock = wakeup_source_register(sprdwl_dev,
"Wi-Fi_cmd_wakelock");
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
cmd->wake_lock = wakeup_source_register(sprdwl_dev, "Wi-Fi_cmd_wakelock");
#else
cmd->wake_lock = wakeup_source_register(sprdwl_dev, "Wi-Fi_cmd_wakelock");
#endif
#endif
if (!cmd->wake_lock) {
wl_err("%s wakeup source register error.\n", __func__);
return -EINVAL;
}
spin_lock_init(&cmd->lock);
mutex_init(&cmd->cmd_lock);
init_completion(&cmd->completed);
cmd->init_ok = 1;
return 0;
}
void sprdwl_cmd_wake_upall(void)
{
complete(&g_sprdwl_cmd.completed);
}
static void sprdwl_cmd_set(struct sprdwl_cmd_hdr *hdr)
{
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
u32 msec;
ktime_t kt;
kt = ktime_get();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
msec = (u32)(div_u64(kt, NSEC_PER_MSEC));
#else
msec = (u32)(div_u64(kt.tv64, NSEC_PER_MSEC));
#endif
hdr->mstime = cpu_to_le32(msec);
spin_lock_bh(&cmd->lock);
kfree(cmd->data);
cmd->data = NULL;
cmd->mstime = msec;
cmd->cmd_id = hdr->cmd_id;
spin_unlock_bh(&cmd->lock);
}
static void sprdwl_cmd_clean(struct sprdwl_cmd *cmd)
{
spin_lock_bh(&cmd->lock);
kfree(cmd->data);
cmd->data = NULL;
cmd->mstime = 0;
cmd->cmd_id = 0;
spin_unlock_bh(&cmd->lock);
}
void sprdwl_cmd_deinit(void)
{
unsigned long timeout;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
atomic_add(SPRDWL_CMD_EXIT_VAL, &cmd->refcnt);
complete(&cmd->completed);
timeout = jiffies + msecs_to_jiffies(1000);
while (atomic_read(&cmd->refcnt) > SPRDWL_CMD_EXIT_VAL) {
if (time_after(jiffies, timeout)) {
wl_err("%s cmd lock timeout\n", __func__);
break;
}
usleep_range(2000, 2500);
}
sprdwl_cmd_clean(cmd);
mutex_destroy(&cmd->cmd_lock);
if (cmd->wake_lock)
wakeup_source_unregister(cmd->wake_lock);
}
extern struct sprdwl_intf_ops g_intf_ops;
static int sprdwl_cmd_lock(struct sprdwl_cmd *cmd)
{
struct sprdwl_intf *intf = (struct sprdwl_intf *)g_intf_ops.intf;
if (atomic_inc_return(&cmd->refcnt) >= SPRDWL_CMD_EXIT_VAL) {
atomic_dec(&cmd->refcnt);
wl_err("%s failed, cmd->refcnt=%d\n",
__func__,
atomic_read(&cmd->refcnt));
return -1;
}
mutex_lock(&cmd->cmd_lock);
if ((intf->priv->is_suspending == 0) && (sprdwcn_bus_get_wl_wake_host_en() == SPRDWL_WAKE_HOST))
__pm_stay_awake(cmd->wake_lock);
if (SPRDWL_PS_SUSPENDED == intf->suspend_mode) {
reinit_completion(&intf->suspend_completed);
wait_for_completion(&intf->suspend_completed);
wl_info("wait for completion\n");
}
wl_debug("cmd->refcnt=%x\n", atomic_read(&cmd->refcnt));
return 0;
}
static void sprdwl_cmd_unlock(struct sprdwl_cmd *cmd)
{
struct sprdwl_intf *intf = (struct sprdwl_intf *)g_intf_ops.intf;
mutex_unlock(&cmd->cmd_lock);
atomic_dec(&cmd->refcnt);
if ((intf->priv->is_suspending == 0) && (sprdwcn_bus_get_wl_wake_host_en() == SPRDWL_WAKE_HOST))
__pm_relax(cmd->wake_lock);
if (intf->priv->is_suspending == 1)
intf->priv->is_suspending = 0;
}
struct sprdwl_msg_buf *__sprdwl_cmd_getbuf(struct sprdwl_priv *priv,
u16 len, u8 ctx_id,
enum sprdwl_head_rsp rsp,
u8 cmd_id, gfp_t flags)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_hdr *hdr;
u16 plen = sizeof(*hdr) + len;
enum sprdwl_mode mode = SPRDWL_MODE_NONE;/*default to open new device*/
#if defined(UWE5621_FTR)
void *data = NULL;
struct sprdwl_vif *vif;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
if (intf->cp_asserted == 1)
return NULL;
#ifdef CP2_RESET_SUPPORT
if(g_sprdwl_priv->sync.cp2_reset_flag == true) {
if((cmd_id != WIFI_CMD_SYNC_VERSION) &&
(cmd_id != WIFI_CMD_DOWNLOAD_INI) &&
(cmd_id != WIFI_CMD_GET_INFO) &&
(cmd_id != WIFI_CMD_OPEN) &&
(cmd_id != WIFI_CMD_SET_REGDOM)) {
return NULL;
}
}
#endif /*CP2_RESET_SUPPORT*/
if (cmd_id >= WIFI_CMD_OPEN) {
vif = ctx_id_to_vif(priv, ctx_id);
if (!vif)
wl_err("%s cant't get vif, ctx_id: %d\n",
__func__, ctx_id);
else
mode = vif->mode;
sprdwl_put_vif(vif);
}
#endif
msg = sprdwl_intf_get_msg_buf(priv, SPRDWL_TYPE_CMD, mode, ctx_id);
if (!msg) {
wl_err("%s, %d, getmsgbuf fail, mode=%d\n",
__func__, __LINE__, mode);
return NULL;
}
#if defined(UWE5621_FTR)
data = kzalloc((plen + priv->hw_offset), flags);
if (data) {
hdr = (struct sprdwl_cmd_hdr *)(data + priv->hw_offset);
hdr->common.type = SPRDWL_TYPE_CMD;
hdr->common.reserv = 0;
hdr->common.rsp = rsp;
hdr->common.ctx_id = ctx_id;
hdr->plen = cpu_to_le16(plen);
hdr->cmd_id = cmd_id;
sprdwl_fill_msg(msg, NULL, data, plen);
msg->data = hdr + 1;
} else {
wl_err("%s failed to allocate skb\n", __func__);
sprdwl_intf_free_msg_buf(priv, msg);
return NULL;
}
#else
msg->skb = dev_alloc_skb(plen);
if (msg->skb) {
memset(msg->skb->data, 0, plen);
hdr = (struct sprdwl_cmd_hdr *)msg->skb->data;
hdr->common.type = SPRDWL_TYPE_CMD;
hdr->common.reserv = 0;
hdr->common.rsp = rsp;
hdr->common.ctx_id = ctx_id;
hdr->plen = cpu_to_le16(plen);
hdr->cmd_id = cmd_id;
sprdwl_fill_msg(msg, msg->skb, msg->skb->data, plen);
msg->data = hdr + 1;
} else {
wl_err("%s failed to allocate skb\n", __func__);
sprdwl_intf_free_msg_buf(priv, msg);
return NULL;
}
#endif
return msg;
}
/* if erro, data is released in this function
* if OK, data is released in hif interface
*/
static int sprdwl_cmd_send_to_ic(struct sprdwl_priv *priv,
struct sprdwl_msg_buf *msg)
{
struct sprdwl_cmd_hdr *hdr;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
#if defined(UWE5621_FTR)
hdr = (struct sprdwl_cmd_hdr *)(msg->tran_data + priv->hw_offset);
#else
hdr = (struct sprdwl_cmd_hdr *)msg->skb->data;
#endif
/*TODO:consider common this if condition since
* SPRDWL_HEAD_NORSP not used any more
*/
if (hdr->common.rsp)
sprdwl_cmd_set(hdr);
wl_warn("[%u]ctx_id %d send[%s], num: %d\n",
le32_to_cpu(hdr->mstime),
hdr->common.ctx_id,
cmd2str(hdr->cmd_id),
tx_msg->cmd_send + 1);
return sprdwl_send_cmd(priv, msg);
}
static int sprdwl_timeout_recv_rsp(struct sprdwl_priv *priv,
unsigned int timeout)
{
int ret;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
ret = wait_for_completion_timeout(&cmd->completed, msecs_to_jiffies(timeout));
if (!ret) {
wl_err("[%s]timeout\n", cmd2str(cmd->cmd_id));
return -1;
} else if (sprdwl_intf_is_exit(priv) ||
atomic_read(&cmd->refcnt) >= SPRDWL_CMD_EXIT_VAL) {
wl_err("cmd->refcnt=%x\n", atomic_read(&cmd->refcnt));
return -1;
} else if (tx_msg->hang_recovery_status == HANG_RECOVERY_ACKED
&& cmd->cmd_id != WIFI_CMD_HANG_RECEIVED) {
wl_warn("hang recovery happen\n");
return -1;
}
spin_lock_bh(&cmd->lock);
ret = cmd->data ? 0 : -1;
spin_unlock_bh(&cmd->lock);
return ret;
}
static int sprdwl_atcmd_assert(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 cmd_id, u8 reason)
{
#define ASSERT_INFO_BUF_SIZE 100
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
char buf[ASSERT_INFO_BUF_SIZE] = {0};
u8 idx = 0;
wl_err("%s ctx_id:%d, cmd_id:%d, reason:%d, cp_asserted:%d\n",
__func__, vif_ctx_id, cmd_id, reason, intf->cp_asserted);
if (intf->cp_asserted == 0) {
intf->cp_asserted = 1;
if ((strlen(cmd2str(cmd_id)) + strlen(assert_reason_to_str(reason)) +
strlen("[CMD] ") + strlen(", [REASON] ")) < ASSERT_INFO_BUF_SIZE)
idx += sprintf(buf+idx, "[CMD] %s, [REASON] %s", cmd2str(cmd_id), assert_reason_to_str(reason));
else
idx += sprintf(buf+idx, "[CMD ID] %d, [REASON ID] %d", cmd_id, reason);
buf[idx] = '\0';
mdbg_assert_interface(buf);
sprdwl_net_flowcontrl(priv, SPRDWL_MODE_NONE, false);
#ifndef CP2_RESET_SUPPORT
intf->exit = 1;
#endif /*CP2_RESET_SUPPORT*/
return 1;
} else {
return -1;
}
#undef ASSERT_INFO_BUF_SIZE
}
/* msg is released in this function or the realy driver
* rbuf: the msg after sprdwl_cmd_hdr
* rlen: input the length of rbuf
* output the length of the msg,if *rlen == 0, rbuf get nothing
*/
int sprdwl_cmd_send_recv(struct sprdwl_priv *priv,
struct sprdwl_msg_buf *msg,
unsigned int timeout, u8 *rbuf, u16 *rlen)
{
u8 cmd_id;
u16 plen;
int ret = 0;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
struct sprdwl_cmd_hdr *hdr;
u8 ctx_id;
struct sprdwl_vif *vif;
struct sprdwl_intf *intf;
struct sprdwl_tx_msg *tx_msg;
intf = (struct sprdwl_intf *)(priv->hw_priv);
tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
ret = sprdwl_api_available_check(priv, msg);
if (ret || sprdwl_cmd_lock(cmd)) {
sprdwl_intf_free_msg_buf(priv, msg);
#if defined(UWE5621_FTR)
kfree(msg->tran_data);
#else
dev_kfree_skb(msg->skb);
#endif
if (rlen)
*rlen = 0;
if (ret)
wl_err("API check fail, return!!\n");
goto out;
}
#if defined(UWE5621_FTR)
hdr = (struct sprdwl_cmd_hdr *)(msg->tran_data + priv->hw_offset);
#else
hdr = (struct sprdwl_cmd_hdr *)msg->skb->data;
#endif
cmd_id = hdr->cmd_id;
ctx_id = hdr->common.ctx_id;
reinit_completion(&cmd->completed);
ret = sprdwl_cmd_send_to_ic(priv, msg);
if (ret) {
sprdwl_cmd_unlock(cmd);
wl_err("%s ctx_id = %d, cmd: %s[%d] send failed, ret = %d\n",
__func__, ctx_id, cmd2str(cmd_id), cmd_id, ret);
return -1;
}
ret = sprdwl_timeout_recv_rsp(priv, timeout);
if(intf->cp_asserted == 1) {
wl_err("%s, cp_asserted:%d\n", __func__, intf->cp_asserted);
} else if (ret != -1) {
if (rbuf && rlen && *rlen) {
hdr = (struct sprdwl_cmd_hdr *)cmd->data;
plen = le16_to_cpu(hdr->plen) - sizeof(*hdr);
*rlen = min(*rlen, plen);
memcpy(rbuf, hdr->paydata, *rlen);
wl_warn("ctx_id:%d cmd_id:%d [%s]rsp received, num=%d\n",
hdr->common.ctx_id, cmd_id, cmd2str(cmd_id),
tx_msg->cmd_send);
if (cmd_id == WIFI_CMD_OPEN)
rbuf[0] = hdr->common.ctx_id;
} else {
hdr = (struct sprdwl_cmd_hdr *)cmd->data;
wl_info("ctx_id:%d cmd_id:%d [%s]rsp received, num=%d\n",
hdr->common.ctx_id, cmd_id, cmd2str(cmd_id),
tx_msg->cmd_send);
}
} else {
wl_err("ctx_id:%d cmd: %s[%d] rsp timeout (mstime = %d), num=%d\n",
ctx_id, cmd2str(cmd_id), cmd_id, le32_to_cpu(cmd->mstime),
tx_msg->cmd_send);
if (cmd_id == WIFI_CMD_CLOSE) {
sprdwl_atcmd_assert(priv, ctx_id, cmd_id, CMD_RSP_TIMEOUT_ERROR);
sprdwl_cmd_unlock(cmd);
return ret;
}
vif = ctx_id_to_vif(priv, ctx_id);
if (vif != NULL) {
intf = (struct sprdwl_intf *)(vif->priv->hw_priv);
tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
if (intf->cp_asserted == 0 &&
tx_msg->hang_recovery_status == HANG_RECOVERY_END &&
!intf->exit)
sprdwl_send_assert_cmd(vif, cmd_id, CMD_RSP_TIMEOUT_ERROR);
sprdwl_put_vif(vif);
}
}
sprdwl_cmd_unlock(cmd);
out:
return ret;
}
/* msg is released in this function or the realy driver
* rbuf: the msg after sprdwl_cmd_hdr
* rlen: input the length of rbuf
* output the length of the msg,if *rlen == 0, rbuf get nothing
*/
int sprdwl_cmd_send_recv_no_wait(struct sprdwl_priv *priv,
struct sprdwl_msg_buf *msg)
{
u8 cmd_id;
int ret = 0;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
struct sprdwl_cmd_hdr *hdr;
u8 ctx_id;
if (sprdwl_cmd_lock(cmd)) {
wl_err("%s, %d, error!\n", __func__, __LINE__);
sprdwl_intf_free_msg_buf(priv, msg);
#if defined(UWE5621_FTR)
kfree(msg->tran_data);
#else
dev_kfree_skb(msg->skb);
#endif
goto out;
}
#if defined(UWE5621_FTR)
hdr = (struct sprdwl_cmd_hdr *)(msg->tran_data + priv->hw_offset);
#else
hdr = (struct sprdwl_cmd_hdr *)msg->skb->data;
#endif
cmd_id = hdr->cmd_id;
ctx_id = hdr->common.ctx_id;
ret = sprdwl_cmd_send_to_ic(priv, msg);
if (ret) {
sprdwl_cmd_unlock(cmd);
return -1;
}
sprdwl_cmd_unlock(cmd);
out:
return ret;
}
/*Commands to sync API version with firmware*/
int sprdwl_sync_version(struct sprdwl_priv *priv)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_api_t *drv_api = NULL;
struct sprdwl_cmd_api_t *fw_api = NULL;
u16 r_len = sizeof(*fw_api);
u8 r_buf[sizeof(*fw_api)];
int ret = 0;
msg = sprdwl_cmd_getbuf(priv, sizeof(struct sprdwl_cmd_api_t),
SPRDWL_MODE_NONE, SPRDWL_HEAD_RSP,
WIFI_CMD_SYNC_VERSION);
if (!msg)
return -ENOMEM;
drv_api = (struct sprdwl_cmd_api_t *)msg->data;
/*fill drv api version got from local*/
sprdwl_fill_drv_api_version(priv, drv_api);
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, &r_len);
if (!ret && r_len) {
fw_api = (struct sprdwl_cmd_api_t *)r_buf;
/*fill fw api version to priv got from firmware*/
sprdwl_fill_fw_api_version(priv, fw_api);
}
return ret;
}
/* Commands */
static int sprdwl_down_ini_cmd(struct sprdwl_priv *priv,
uint8_t *data, uint32_t len,
uint8_t sec_num)
{
int ret = 0;
struct sprdwl_msg_buf *msg;
uint8_t *p = NULL;
uint16_t CRC = 0;
/*reserved 4 byte of section num for align */
msg = sprdwl_cmd_getbuf(priv, len + 4 + sizeof(CRC),
SPRDWL_MODE_NONE, SPRDWL_HEAD_RSP,
WIFI_CMD_DOWNLOAD_INI);
if (!msg)
return -ENOMEM;
/*calc CRC value*/
CRC = CRC16(data, len);
wl_info("CRC value:%d\n", CRC);
p = msg->data;
*p = sec_num;
/*copy data after section num*/
memcpy((p + 4), data, len);
/*put CRC value at the tail of INI data*/
memcpy((p + 4 + len), &CRC, sizeof(CRC));
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
return ret;
}
void sprdwl_download_ini(struct sprdwl_priv *priv)
{
#define SEC1 1
#define SEC2 2
#define SEC3 3
int ret;
struct wifi_conf_t *wifi_data;
struct wifi_conf_sec1_t *sec1;
struct wifi_conf_sec2_t *sec2;
wl_debug("%s enter:", __func__);
/*if ini file has been download already, return*/
if (sprdwl_get_ini_status(priv)) {
wl_err("RF ini download already, skip!\n");
return;
}
wifi_data = kzalloc(sizeof(struct wifi_conf_t), GFP_KERNEL);
/*init INI data struct */
/*got ini data from file*/
ret = get_wifi_config_param(wifi_data);
if (ret) {
wl_err("load ini data failed, return\n");
kfree(wifi_data);
wlan_set_assert(priv, SPRDWL_MODE_NONE,
WIFI_CMD_DOWNLOAD_INI,
LOAD_INI_DATA_FAILED);
return;
}
wl_info("total config len:%ld,sec1 len:%ld, sec2 len:%ld\n",
(long unsigned int)sizeof(wifi_data), (long unsigned int)sizeof(*sec1),
(long unsigned int)sizeof(*sec2));
/*devide wifi_conf into sec1 and sec2 since it's too large*/
sec1 = (struct wifi_conf_sec1_t *)wifi_data;
sec2 = (struct wifi_conf_sec2_t *)(&wifi_data->tx_scale);
wl_info("download the first section of config file\n");
ret = sprdwl_down_ini_cmd(priv, (uint8_t *)sec1, sizeof(*sec1), SEC1);
if (ret) {
wl_err("download the first section of ini fail,return\n");
kfree(wifi_data);
wlan_set_assert(priv, SPRDWL_MODE_NONE,
WIFI_CMD_DOWNLOAD_INI,
DOWNLOAD_INI_DATA_FAILED);
return;
}
wl_info("download the second section of config file\n");
ret = sprdwl_down_ini_cmd(priv, (uint8_t *)sec2, sizeof(*sec2), SEC2);
if (ret) {
wl_err("download the second section of ini fail,return\n");
kfree(wifi_data);
wlan_set_assert(priv, SPRDWL_MODE_NONE,
WIFI_CMD_DOWNLOAD_INI,
DOWNLOAD_INI_DATA_FAILED);
return;
}
if (wifi_data->rf_config.rf_data_len) {
wl_info("download the third section of config file\n");
wl_info("rf_data_len = %d\n", wifi_data->rf_config.rf_data_len);
ret = sprdwl_down_ini_cmd(priv, wifi_data->rf_config.rf_data,
wifi_data->rf_config.rf_data_len, SEC3);
if (ret) {
wl_err("download the third section of ini fail,return\n");
kfree(wifi_data);
wlan_set_assert(priv, SPRDWL_MODE_NONE,
WIFI_CMD_DOWNLOAD_INI,
DOWNLOAD_INI_DATA_FAILED);
return;
}
}
kfree(wifi_data);
}
int sprdwl_get_fw_info(struct sprdwl_priv *priv)
{
int ret;
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_fw_info *p;
struct sprdwl_tlv_data *tlv;
u16 r_len = sizeof(*p) + GET_INFO_TLV_RBUF_SIZE;
u16 r_len_ori = r_len;
u8 r_buf[sizeof(*p) + GET_INFO_TLV_RBUF_SIZE];
#ifdef COMPAT_SAMPILE_CODE
u8 compat_ver = 0;
#endif
unsigned int len_count = 0;
bool b_tlv_data_chk = true;
u16 tlv_len;
#ifdef WL_CONFIG_DEBUG
u8 ap_version = NOTIFY_AP_VERSION_USER_DEBUG;
#else
u8 ap_version = NOTIFY_AP_VERSION_USER;
#endif
u16 offset = 0;
#ifdef OTT_UWE
u8 bytes_allign = 1;
#define OTT_UWE_OFFSET_ENABLE 1
#endif
u8 credit_via_data = 1;
tlv_len = sizeof(*tlv) + 1;
#ifdef OTT_UWE
tlv_len += (sizeof(*tlv) + 1);
#endif
if (priv->hw_type == SPRDWL_HW_USB)
tlv_len += (sizeof(*tlv) + 1);
memset(r_buf, 0, r_len);
msg = sprdwl_cmd_getbuf(priv, tlv_len, SPRDWL_MODE_NONE,
SPRDWL_HEAD_RSP, WIFI_CMD_GET_INFO);
if (!msg)
return -ENOMEM;
#ifdef COMPAT_SAMPILE_CODE
compat_ver = need_compat_operation(priv, WIFI_CMD_GET_INFO);
if (compat_ver) {
switch (compat_ver) {
case VERSION_1:
/*add data struct modification in here!*/
priv->sync_api.compat = VERSION_1;
break;
case VERSION_2:
/*add data struct modification in here!*/
priv->sync_api.compat = VERSION_2;
break;
case VERSION_3:
/*add data struct modification in here!*/
priv->sync_api.compat = VERSION_3;
break;
default:
break;
}
}
#endif
/*to notify CP2 use more CP2 buffer*/
sprdwl_set_tlv_elmt((u8 *)(msg->data + offset), NOTIFY_AP_VERSION,
sizeof(ap_version), &ap_version);
offset += (sizeof(*tlv) + 1);
#ifdef OTT_UWE
/*to notify CP2 this is OTT version ,4bytes allign*/
sprdwl_set_tlv_elmt((u8 *)(msg->data + offset), OTT_UWE_OFFSET_ENABLE,
sizeof(bytes_allign), &bytes_allign);
offset += (sizeof(*tlv) + 1);
#endif
if (priv->hw_type == SPRDWL_HW_USB) {
/*to notify CP2 data credit disable*/
sprdwl_set_tlv_elmt((u8 *)(msg->data + offset), NOTIFY_CREDIT_VIA_RX_DATA,
sizeof(credit_via_data), &credit_via_data);
offset += (sizeof(*tlv) + 1);
}
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, &r_len);
if (!ret && r_len) {
#if defined COMPAT_SAMPILE_CODE
switch (compat_ver) {
case VERSION_1:
/*add data struct modification in here!*/
break;
case VERSION_2:
/*add data struct modification in here!*/
break;
case VERSION_3:
/*add data struct modification in here!*/
break;
default:
break;
}
#endif
/* Version 1 Section */
p = (struct sprdwl_cmd_fw_info *)r_buf;
priv->chip_model = p->chip_model;
priv->chip_ver = p->chip_version;
priv->fw_ver = p->fw_version;
priv->fw_capa = p->fw_capa;
priv->fw_std = p->fw_std;
priv->extend_feature = p->extend_feature;
priv->max_ap_assoc_sta = p->max_ap_assoc_sta;
priv->max_acl_mac_addrs = p->max_acl_mac_addrs;
priv->max_mc_mac_addrs = p->max_mc_mac_addrs;
priv->wnm_ft_support = p->wnm_ft_support;
len_count += SEC1_LEN;
/*check sec2 data length got from fw*/
if ((r_len - len_count) >= sizeof(struct wiphy_sec2_t)) {
priv->wiphy_sec2_flag = 1;
wl_info("save wiphy section2 info to sprdwl_priv\n");
memcpy(&priv->wiphy_sec2, &p->wiphy_sec2,
sizeof(struct wiphy_sec2_t));
} else {
goto out;
}
len_count += sizeof(struct wiphy_sec2_t);
if ((r_len - len_count) >= ETH_ALEN) {
ether_addr_copy(priv->mac_addr, p->mac_addr);
} else {
memset(priv->mac_addr, 0x00, ETH_ALEN);
goto out;
}
len_count += ETH_ALEN;
if ((r_len - len_count) >= 1)
priv->credit_capa = p->credit_capa;
else
priv->credit_capa = TX_WITH_CREDIT;
/* Version 2 Section */
if (compat_ver == VERSION_1) {
/* Set default value for non-version-1 variable */
priv->ott_supt = OTT_NO_SUPT;
} else {
len_count = sizeof(struct sprdwl_cmd_fw_info);
tlv = (struct sprdwl_tlv_data *)((u8 *)r_buf + len_count);
while ((len_count + sizeof(struct sprdwl_tlv_data) + tlv->len) <= r_len) {
b_tlv_data_chk = false;
switch (tlv->type) {
case GET_INFO_TLV_TP_OTT:
if (tlv->len == 1) {
priv->ott_supt = *((unsigned char *)(tlv->data));
b_tlv_data_chk = true;
}
break;
default:
break;
}
wl_info("%s, TLV type=%d, len=%d, data_chk=%d\n",
__func__, tlv->type, tlv->len, b_tlv_data_chk);
if (b_tlv_data_chk == false) {
wl_err("%s TLV check failed: type=%d, len=%d\n",
__func__, tlv->type, tlv->len);
goto out;
}
len_count += (sizeof(struct sprdwl_tlv_data) + tlv->len);
tlv = (struct sprdwl_tlv_data *)((u8 *)r_buf + len_count);
}
if (r_len_ori <= r_len) {
wl_warn("%s check tlv rbuf size: r_len_ori=%d, r_len=%d\n",
__func__, r_len_ori, r_len);
}
if (len_count != r_len) {
wl_err("%s length mismatch: len_count=%d, r_len=%d\n",
__func__, len_count, r_len);
goto out;
}
}
out:
wl_err("%s, drv_version=%d, fw_version=%d, compat_ver=%d\n", __func__,
(&priv->sync_api)->api_array[WIFI_CMD_GET_INFO].drv_version,
(&priv->sync_api)->api_array[WIFI_CMD_GET_INFO].fw_version,
compat_ver);
wl_err("chip_model:0x%x, chip_ver:0x%x\n",
priv->chip_model, priv->chip_ver);
wl_err("fw_ver:%d, fw_std:0x%x, fw_capa:0x%x\n",
priv->fw_ver, priv->fw_std, priv->fw_capa);
if (is_valid_ether_addr(priv->mac_addr))
wl_err("mac_addr:%02x:%02x:%02x:%02x:%02x:%02x\n",
priv->mac_addr[0], priv->mac_addr[1], priv->mac_addr[2],
priv->mac_addr[3], priv->mac_addr[4], priv->mac_addr[5]);
wl_err("credit_capa:%s, extend_feature:0x%x\n",
(priv->credit_capa == TX_WITH_CREDIT) ?
"TX_WITH_CREDIT" : "TX_NO_CREDIT", priv->extend_feature);
wl_err("ott support:%d\n", priv->ott_supt);
}
return ret;
}
int sprdwl_set_regdom(struct sprdwl_priv *priv, u8 *regdom, u32 len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_ieee80211_regdomain *p;
#ifdef COMPAT_SAMPILE_CODE
u8 compat_ver = 0;
#endif
msg = sprdwl_cmd_getbuf(priv, len, SPRDWL_MODE_NONE, SPRDWL_HEAD_RSP,
WIFI_CMD_SET_REGDOM);
if (!msg)
return -ENOMEM;
#ifdef COMPAT_SAMPILE_CODE
compat_ver = need_compat_operation(priv, WIFI_CMD_SET_REGDOM);
if (compat_ver) {
switch (compat_ver) {
case VERSION_1:
/*add data struct modification in here!*/
priv->sync_api.compat = VERSION_1;
break;
case VERSION_2:
/*add data struct modification in here!*/
priv->sync_api.compat = VERSION_2;
break;
case VERSION_3:
/*add data struct modification in here!*/
priv->sync_api.compat = VERSION_3;
break;
default:
break;
}
}
#endif
p = (struct sprdwl_ieee80211_regdomain *)msg->data;
memcpy(p, regdom, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_open_fw(struct sprdwl_priv *priv, u8 *vif_ctx_id,
u8 mode, u8 *mac_addr)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_open *p;
u16 rlen = 1;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), *vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_OPEN);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_open *)msg->data;
p->mode = mode;
if (mac_addr)
memcpy(&p->mac[0], mac_addr, sizeof(p->mac));
else
wl_err("%s, %d, mac_addr error!\n", __func__, __LINE__);
p->reserved = 0;
if (0 != wfa_cap) {
p->reserved = wfa_cap;
wfa_cap = 0;
}
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT,
vif_ctx_id, &rlen);
}
int sprdwl_close_fw(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 mode)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_close *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_CLOSE);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_close *)msg->data;
p->mode = mode;
sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
/* FIXME - in case of close failure */
return 0;
}
int sprdwl_power_save(struct sprdwl_priv *priv, u8 vif_ctx_id,
u8 sub_type, u8 status)
{
int ret;
s32 ret_code;
u16 len = 0;
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_power_save *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_POWER_SAVE);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_power_save *)msg->data;
p->sub_type = sub_type;
p->value = status;
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, (u8 *)&ret_code, &len);
return len == 4 ? ret_code : ret;
}
int sprdwl_add_key(struct sprdwl_priv *priv, u8 vif_ctx_id, const u8 *key_data,
u8 key_len, u8 pairwise, u8 key_index, const u8 *key_seq,
u8 cypher_type, const u8 *mac_addr)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_add_key *p;
u8 *sub_cmd;
int datalen = sizeof(*p) + sizeof(*sub_cmd) + key_len;
msg = sprdwl_cmd_getbuf(priv, datalen, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_KEY);
if (!msg)
return -ENOMEM;
sub_cmd = (u8 *)msg->data;
*sub_cmd = SPRDWL_SUBCMD_ADD;
p = (struct sprdwl_cmd_add_key *)(++sub_cmd);
p->key_index = key_index;
p->pairwise = pairwise;
p->cypher_type = cypher_type;
p->key_len = key_len;
if (key_seq) {
if (SPRDWL_CIPHER_WAPI == cypher_type)
memcpy(p->keyseq, key_seq, WAPI_PN_SIZE);
else
memcpy(p->keyseq, key_seq, 8);
}
if (mac_addr)
ether_addr_copy(p->mac, mac_addr);
if (key_data)
memcpy(p->value, key_data, key_len);
if (mac_addr)
reset_pn(priv, mac_addr);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_del_key(struct sprdwl_priv *priv, u8 vif_ctx_id, u16 key_index,
bool pairwise, const u8 *mac_addr)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_del_key *p;
u8 *sub_cmd;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + sizeof(*sub_cmd), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_KEY);
if (!msg)
return -ENOMEM;
sub_cmd = (u8 *)msg->data;
*sub_cmd = SPRDWL_SUBCMD_DEL;
p = (struct sprdwl_cmd_del_key *)(++sub_cmd);
p->key_index = key_index;
p->pairwise = pairwise;
if (mac_addr)
ether_addr_copy(p->mac, mac_addr);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_def_key(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 key_index)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_def_key *p;
u8 *sub_cmd;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + sizeof(*sub_cmd), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_KEY);
if (!msg)
return -ENOMEM;
sub_cmd = (u8 *)msg->data;
*sub_cmd = SPRDWL_SUBCMD_SET;
p = (struct sprdwl_cmd_set_def_key *)(++sub_cmd);
p->key_index = key_index;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_rekey_data(struct sprdwl_priv *priv, u8 vif_ctx_id,
struct cfg80211_gtk_rekey_data *data)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_rekey *p;
u8 *sub_cmd;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + sizeof(*sub_cmd), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_KEY);
if (!msg)
return -ENOMEM;
sub_cmd = (u8 *)msg->data;
*sub_cmd = SPRDWL_SUBCMD_REKEY;
p = (struct sprdwl_cmd_set_rekey *)(++sub_cmd);
memcpy(p->kek, data->kek, NL80211_KEK_LEN);
memcpy(p->kck, data->kck, NL80211_KCK_LEN);
memcpy(p->replay_ctr, data->replay_ctr, NL80211_REPLAY_CTR_LEN);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_ie(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 type,
const u8 *ie, u16 len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_ie *p;
int i = 0;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_IE);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_set_ie *)msg->data;
p->type = type;
p->len = len;
memcpy(p->data, ie, len);
/*set ext cap ie bit80-bit87 to 0, otherwise connect fail*/
if (type == SPRDWL_IE_ASSOC_REQ) {
i = 0;
while (i < len) {
if (p->data[i] == 0x7f) {
if (p->data[i+1] >= 0x0b) {
p->data[i+12] = 0x00;
}
break;
}
i += (p->data[i+1] + 2);
}
wl_hex_dump(L_DBG, "ASSOC IE: ", DUMP_PREFIX_OFFSET, 16, 1, p->data, len, 0);
}
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
#ifdef DFS_MASTER
int sprdwl_reset_beacon(struct sprdwl_priv *priv,
u8 vif_ctx_id, const u8 *beacon, u16 len)
{
struct sprdwl_msg_buf *msg;
msg = sprdwl_cmd_getbuf(priv, len, vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_RESET_BEACON);
if (!msg)
return -ENOMEM;
memcpy(msg->data, beacon, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
#endif
int sprdwl_start_ap(struct sprdwl_priv *priv,
u8 vif_ctx_id, u8 *beacon, u16 len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_start_ap *p;
u16 datalen = sizeof(*p) + len;
msg = sprdwl_cmd_getbuf(priv, datalen, vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_START_AP);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_start_ap *)msg->data;
p->len = cpu_to_le16(len);
memcpy(p->value, beacon, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_del_station(struct sprdwl_priv *priv, u8 vif_ctx_id,
const u8 *mac_addr, u16 reason_code)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_del_station *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_DEL_STATION);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_del_station *)msg->data;
if (mac_addr)
memcpy(&p->mac[0], mac_addr, sizeof(p->mac));
p->reason_code = cpu_to_le16(reason_code);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_get_station(struct sprdwl_priv *priv, u8 vif_ctx_id,
struct sprdwl_cmd_get_station *sta)
{
struct sprdwl_msg_buf *msg;
u8 *r_buf = (u8 *)sta;
u16 r_len = sizeof(struct sprdwl_cmd_get_station);
int ret;
msg = sprdwl_cmd_getbuf(priv, 0, vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_GET_STATION);
if (!msg)
return -ENOMEM;
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, &r_len);
return ret;
}
int sprdwl_set_channel(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 channel)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_channel *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_CHANNEL);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_set_channel *)msg->data;
p->channel = channel;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_scan(struct sprdwl_priv *priv, u8 vif_ctx_id,
u32 channels, int ssid_len, const u8 *ssid_list,
u16 chn_count_5g, const u16 *chns_5g)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_scan *p;
struct sprdwl_cmd_rsp_state_code state;
u16 rlen;
u32 data_len, chns_len_5g;
struct sprdwl_5g_chn {
u16 n_5g_chn;
u16 chns[0];
} *ext_5g;
chns_len_5g = chn_count_5g * sizeof(*chns_5g);
data_len = sizeof(*p) + ssid_len + chns_len_5g +
sizeof(ext_5g->n_5g_chn);
msg = sprdwl_cmd_getbuf(priv, data_len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SCAN);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_scan *)msg->data;
p->channels = channels;
if (ssid_len > 0) {
memcpy(p->ssid, ssid_list, ssid_len);
p->ssid_len = cpu_to_le16(ssid_len);
}
ext_5g = (struct sprdwl_5g_chn *)(p->ssid + ssid_len);
if (chn_count_5g > 0) {
ext_5g->n_5g_chn = chn_count_5g;
memcpy(ext_5g->chns, chns_5g, chns_len_5g);
} else {
ext_5g->n_5g_chn = 0;
}
wl_hex_dump(L_DBG, "scan hex:", DUMP_PREFIX_OFFSET,
16, 1, p, data_len, true);
rlen = sizeof(state);
return sprdwl_cmd_send_recv(priv, msg, CMD_SCAN_WAIT_TIMEOUT,
(u8 *)&state, &rlen);
}
int sprdwl_sched_scan_start(struct sprdwl_priv *priv, u8 vif_ctx_id,
struct sprdwl_sched_scan_buf *buf)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_sched_scan_hd *sscan_head = NULL;
struct sprdwl_cmd_sched_scan_ie_hd *ie_head = NULL;
struct sprdwl_cmd_sched_scan_ifrc *sscan_ifrc = NULL;
u16 datalen;
u8 *p = NULL;
int len = 0, i, hd_len;
datalen = sizeof(*sscan_head) + sizeof(*ie_head) + sizeof(*sscan_ifrc)
+ buf->n_ssids * IEEE80211_MAX_SSID_LEN
+ buf->n_match_ssids * IEEE80211_MAX_SSID_LEN + buf->ie_len;
hd_len = sizeof(*ie_head);
datalen = datalen + (buf->n_ssids ? hd_len : 0)
+ (buf->n_match_ssids ? hd_len : 0)
+ (buf->ie_len ? hd_len : 0);
msg = sprdwl_cmd_getbuf(priv, datalen, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SCHED_SCAN);
if (!msg)
return -ENOMEM;
p = msg->data;
sscan_head = (struct sprdwl_cmd_sched_scan_hd *)(p + len);
sscan_head->started = 1;
sscan_head->buf_flags = SPRDWL_SCHED_SCAN_BUF_END;
len += sizeof(*sscan_head);
ie_head = (struct sprdwl_cmd_sched_scan_ie_hd *)(p + len);
ie_head->ie_flag = SPRDWL_SEND_FLAG_IFRC;
ie_head->ie_len = sizeof(*sscan_ifrc);
len += sizeof(*ie_head);
sscan_ifrc = (struct sprdwl_cmd_sched_scan_ifrc *)(p + len);
sscan_ifrc->interval = buf->interval;
sscan_ifrc->flags = buf->flags;
sscan_ifrc->rssi_thold = buf->rssi_thold;
memcpy(sscan_ifrc->chan, buf->channel, TOTAL_2G_5G_CHANNEL_NUM + 1);
len += ie_head->ie_len;
if (buf->n_ssids > 0) {
ie_head = (struct sprdwl_cmd_sched_scan_ie_hd *)(p + len);
ie_head->ie_flag = SPRDWL_SEND_FLAG_SSID;
ie_head->ie_len = buf->n_ssids * IEEE80211_MAX_SSID_LEN;
len += sizeof(*ie_head);
for (i = 0; i < buf->n_ssids; i++) {
memcpy((p + len + i * IEEE80211_MAX_SSID_LEN),
buf->ssid[i], IEEE80211_MAX_SSID_LEN);
}
len += ie_head->ie_len;
}
if (buf->n_match_ssids > 0) {
ie_head = (struct sprdwl_cmd_sched_scan_ie_hd *)(p + len);
ie_head->ie_flag = SPRDWL_SEND_FLAG_MSSID;
ie_head->ie_len = buf->n_match_ssids * IEEE80211_MAX_SSID_LEN;
len += sizeof(*ie_head);
for (i = 0; i < buf->n_match_ssids; i++) {
memcpy((p + len + i * IEEE80211_MAX_SSID_LEN),
buf->mssid[i], IEEE80211_MAX_SSID_LEN);
}
len += ie_head->ie_len;
}
if (buf->ie_len > 0) {
ie_head = (struct sprdwl_cmd_sched_scan_ie_hd *)(p + len);
ie_head->ie_flag = SPRDWL_SEND_FLAG_IE;
ie_head->ie_len = buf->ie_len;
len += sizeof(*ie_head);
wl_info("%s: ie len is %zu, ie:%s\n",
__func__, buf->ie_len, buf->ie);
memcpy((p + len), buf->ie, buf->ie_len);
len += ie_head->ie_len;
}
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_sched_scan_stop(struct sprdwl_priv *priv, u8 vif_ctx_id)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_sched_scan_hd *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SCHED_SCAN);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_sched_scan_hd *)msg->data;
p->started = 0;
p->buf_flags = SPRDWL_SCHED_SCAN_BUF_END;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_connect(struct sprdwl_priv *priv, u8 vif_ctx_id,
struct sprdwl_cmd_connect *p)
{
struct sprdwl_msg_buf *msg;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_CONNECT);
if (!msg)
return -ENOMEM;
memcpy(msg->data, p, sizeof(*p));
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_disconnect(struct sprdwl_priv *priv, u8 vif_ctx_id, u16 reason_code)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_disconnect *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_DISCONNECT);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_disconnect *)msg->data;
p->reason_code = cpu_to_le16(reason_code);
return sprdwl_cmd_send_recv(priv, msg, CMD_DISCONNECT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_param(struct sprdwl_priv *priv, u32 rts, u32 frag)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_param *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), SPRDWL_MODE_NONE,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_PARAM);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_set_param *)msg->data;
p->rts = cpu_to_le32(rts);
p->frag = cpu_to_le32(frag);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_pmksa(struct sprdwl_priv *priv, u8 vif_ctx_id,
const u8 *bssid, const u8 *pmkid, u8 type)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_pmkid *p;
u8 *sub_cmd;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + sizeof(*sub_cmd), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_PMKSA);
if (!msg)
return -ENOMEM;
sub_cmd = (u8 *)msg->data;
*sub_cmd = type;
p = (struct sprdwl_cmd_pmkid *)(++sub_cmd);
if (bssid)
memcpy(p->bssid, bssid, sizeof(p->bssid));
if (pmkid)
memcpy(p->pmkid, pmkid, sizeof(p->pmkid));
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_qos_map(struct sprdwl_priv *priv, u8 vif_ctx_id, void *qos_map)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_qos_map *p;
int index;
if (!qos_map)
return 0;
msg =
sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, 1,
WIFI_CMD_SET_QOS_MAP);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprdwl_cmd_qos_map *)msg->data;
#else
p = (struct sprdwl_cmd_qos_map *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
memset((u8 *)p, 0, sizeof(*p));
memcpy((u8 *)p, qos_map, sizeof(*p));
memcpy(&g_11u_qos_map.qos_exceptions[0], &p->dscp_exception[0],
sizeof(struct sprdwl_cmd_dscp_exception) * QOS_MAP_MAX_DSCP_EXCEPTION);
for (index = 0; index < 8; index++) {
g_11u_qos_map.qos_ranges[index].low = p->up[index].low;
g_11u_qos_map.qos_ranges[index].high = p->up[index].high;
g_11u_qos_map.qos_ranges[index].up = index;
}
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_gscan_subcmd(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 subcmd, u16 len, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len,
vif_ctx_id, SPRDWL_HEAD_RSP, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = subcmd;
if (data != NULL) {
p->data_len = len;
memcpy(p->data, data, len);
} else{
p->data_len = 0;
}
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_set_gscan_config(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 len, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len,
vif_ctx_id, 1, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_GSCAN_SUBCMD_SET_CONFIG;
p->data_len = len;
memcpy(p->data, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_set_gscan_scan_config(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 len, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len,
vif_ctx_id, 1, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)(msg->skb->data +
sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_GSCAN_SUBCMD_SET_SCAN_CONFIG;
p->data_len = len;
memcpy(p->data, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_enable_gscan(struct sprdwl_priv *priv, u8 vif_ctx_id, void *data,
u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + sizeof(int),
vif_ctx_id, 1, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_GSCAN_SUBCMD_ENABLE_GSCAN;
p->data_len = sizeof(int);
memcpy(p->data, data, p->data_len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_get_gscan_capabilities(struct sprdwl_priv *priv, u8 vif_ctx_id,
u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_GSCAN_SUBCMD_GET_CAPABILITIES;
p->data_len = 0;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_get_gscan_channel_list(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
int *band;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p)+sizeof(*band), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_GSCAN_SUBCMD_GET_CHANNEL_LIST;
p->data_len = sizeof(*band);
#if defined(UWE5621_FTR)
band = (int *)(msg->data + sizeof(struct sprd_cmd_gscan_header));
#else
band = (int *)(msg->skb->data + sizeof(struct sprdwl_cmd_hdr) +
sizeof(struct sprd_cmd_gscan_header));
#endif
*band = *((int *)data);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_set_gscan_bssid_hotlist(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 len, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len,
vif_ctx_id, 1, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_GSCAN_SUBCMD_SET_HOTLIST;
p->data_len = len;
memcpy(p->data, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_set_gscan_bssid_blacklist(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 len, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
struct sprd_cmd_gscan_header *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len,
vif_ctx_id, 1, WIFI_CMD_GSCAN);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprd_cmd_gscan_header *)msg->data;
#else
p = (struct sprd_cmd_gscan_header *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
p->subcmd = SPRDWL_WIFI_SUBCMD_SET_BSSID_BLACKLIST;
p->data_len = len;
memcpy(p->data, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_add_tx_ts(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 tsid,
const u8 *peer, u8 user_prio, u16 admitted_time)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_tx_ts *p;
#ifdef WMMAC_WFA_CERTIFICATION
edca_ac_t ac;
#endif
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, 1,
WIFI_CMD_ADD_TX_TS);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprdwl_cmd_tx_ts *)msg->data;
#else
p = (struct sprdwl_cmd_tx_ts *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
memset((u8 *)p, 0, sizeof(*p));
p->tsid = tsid;
ether_addr_copy(p->peer, peer);
p->user_prio = user_prio;
p->admitted_time = cpu_to_le16(admitted_time);
#ifdef WMMAC_WFA_CERTIFICATION
ac = map_priority_to_edca_ac(p->user_prio);
update_wmmac_ts_info(p->tsid, p->user_prio, ac, true, p->admitted_time);
update_admitted_time(priv, p->tsid, p->admitted_time, true);
#endif
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_del_tx_ts(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 tsid,
const u8 *peer)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_tx_ts *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, 1,
WIFI_CMD_DEL_TX_TS);
if (!msg)
return -ENOMEM;
#if defined(UWE5621_FTR)
p = (struct sprdwl_cmd_tx_ts *)msg->data;
#else
p = (struct sprdwl_cmd_tx_ts *)
(msg->skb->data + sizeof(struct sprdwl_cmd_hdr));
#endif
memset((u8 *)p, 0, sizeof(*p));
p->tsid = tsid;
ether_addr_copy(p->peer, peer);
#ifdef WMMAC_WFA_CERTIFICATION
update_admitted_time(priv, p->tsid, p->admitted_time, false);
remove_wmmac_ts_info(p->tsid);
#endif
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_remain_chan(struct sprdwl_priv *priv, u8 vif_ctx_id,
struct ieee80211_channel *channel,
enum nl80211_channel_type channel_type,
u32 duration, u64 *cookie)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_remain_chan *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_REMAIN_CHAN);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_remain_chan *)msg->data;
p->chan = ieee80211_frequency_to_channel(channel->center_freq);
p->chan_type = channel_type;
p->duraion = cpu_to_le32(duration);
p->cookie = cpu_to_le64(*cookie);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_cancel_remain_chan(struct sprdwl_priv *priv,
u8 vif_ctx_id, u64 cookie)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_cancel_remain_chan *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_CANCEL_REMAIN_CHAN);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_cancel_remain_chan *)msg->data;
p->cookie = cpu_to_le64(cookie);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
#if 0
static int sprdwl_tx_data(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 channel,
u8 dont_wait_for_ack, u32 wait, u64 *cookie,
const u8 *buf, size_t len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_mgmt_tx *p;
u16 datalen = sizeof(*p) + len;
msg = sprdwl_cmd_getbuf(priv, datalen, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TX_MGMT);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_mgmt_tx *)msg->data;
p->chan = channel;
p->dont_wait_for_ack = dont_wait_for_ack;
p->wait = cpu_to_le32(wait);
if (cookie)
p->cookie = cpu_to_le64(*cookie);
p->len = cpu_to_le16(len);
memcpy(p->value, buf, len);
return sprdwl_cmd_send_to_ic(priv, msg);
}
#endif
int sprdwl_tx_mgmt(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 channel,
u8 dont_wait_for_ack, u32 wait, u64 *cookie,
const u8 *buf, size_t len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_mgmt_tx *p;
u16 datalen = sizeof(*p) + len;
msg = sprdwl_cmd_getbuf(priv, datalen, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TX_MGMT);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_mgmt_tx *)msg->data;
p->chan = channel;
p->dont_wait_for_ack = dont_wait_for_ack;
p->wait = cpu_to_le32(wait);
if (cookie)
p->cookie = cpu_to_le64(*cookie);
p->len = cpu_to_le16(len);
memcpy(p->value, buf, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_register_frame(struct sprdwl_priv *priv, u8 vif_ctx_id,
u16 type, u8 reg)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_register_frame *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_REGISTER_FRAME);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_register_frame *)msg->data;
p->type = type;
p->reg = reg;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_cqm_rssi(struct sprdwl_priv *priv, u8 vif_ctx_id,
s32 rssi_thold, u32 rssi_hyst)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_cqm_rssi *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_CQM);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_cqm_rssi *)msg->data;
p->rssih = cpu_to_le32(rssi_thold);
p->rssil = cpu_to_le32(rssi_hyst);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_roam_offload(struct sprdwl_priv *priv, u8 vif_ctx_id,
u8 sub_type, const u8 *data, u8 len)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_roam_offload_data *p;
if (!(priv->fw_capa & SPRDWL_CAPA_11R_ROAM_OFFLOAD)) {
wl_err("%s, not supported\n", __func__);
return -ENOTSUPP;
}
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_ROAM_OFFLOAD);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_roam_offload_data *)msg->data;
p->type = sub_type;
p->len = len;
memcpy(p->value, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_tdls_mgmt(struct sprdwl_vif *vif, struct sk_buff *skb)
{
#ifndef UWE5621_FTR
struct sprdwl_msg_buf *msg;
#endif
int ret;
#ifndef UWE5621_FTR
msg = sprdwl_intf_get_msg_buf(vif->priv, SPRDWL_TYPE_DATA,
SPRDWL_MODE_STATION, vif->ctx_id);
if (!msg) {
if (vif->priv->hw_type == SPRDWL_HW_SDIO_BA)
sprdwl_stop_net(vif);
vif->ndev->stats.tx_fifo_errors++;
wl_err("%s, %d, get msg bug failed\n", __func__, __LINE__);
return -NETDEV_TX_BUSY;
}
#endif
/* temp debug use */
if (skb_headroom(skb) < vif->ndev->needed_headroom)
wl_err("%s skb head len err:%d %d\n",
__func__, skb_headroom(skb),
vif->ndev->needed_headroom);
#ifdef UWE5621_FTR
/*send TDLS mgmt through cmd port instead of data port,needed by CP2*/
ret = sprdwl_send_tdlsdata_use_cmd(skb, vif, 1);
#else
/* sprdwl_send_data: offset use 2 for cp bytes align */
ret = sprdwl_send_data(vif, msg, skb, 2, false);
#endif
if (ret) {
wl_err("%s drop msg due to TX Err\n",
__func__);
goto out;
}
vif->ndev->stats.tx_bytes += skb->len;
vif->ndev->stats.tx_packets++;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
vif->ndev->trans_start = jiffies;
#else
netif_trans_update(vif->ndev);
#endif
wl_hex_dump(L_DBG, "TX packet: ", DUMP_PREFIX_OFFSET,
16, 1, skb->data, skb->len, 0);
out:
return ret;
}
int sprdwl_send_tdls_cmd(struct sprdwl_vif *vif, u8 vif_ctx_id, const u8 *peer,
int oper)
{
struct sprdwl_work *misc_work;
struct sprdwl_tdls_work tdls;
tdls.vif_ctx_id = vif_ctx_id;
if (peer)
ether_addr_copy(tdls.peer, peer);
tdls.oper = oper;
misc_work = sprdwl_alloc_work(sizeof(struct sprdwl_tdls_work));
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return -1;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_TDLS_CMD;
memcpy(misc_work->data, &tdls, sizeof(struct sprdwl_tdls_work));
sprdwl_queue_work(vif->priv, misc_work);
return 0;
}
int sprdwl_tdls_oper(struct sprdwl_priv *priv, u8 vif_ctx_id, const u8 *peer,
int oper)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_tdls *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TDLS);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_tdls *)msg->data;
if (peer)
ether_addr_copy(p->da, peer);
p->tdls_sub_cmd_mgmt = oper;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_start_tdls_channel_switch(struct sprdwl_priv *priv, u8 vif_ctx_id,
const u8 *peer_mac, u8 primary_chan,
u8 second_chan_offset, u8 band)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_tdls *p;
struct sprdwl_cmd_tdls_channel_switch chan_switch;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + sizeof(chan_switch),
vif_ctx_id, SPRDWL_HEAD_RSP, WIFI_CMD_TDLS);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_tdls *)msg->data;
p->tdls_sub_cmd_mgmt = SPRDWL_TDLS_START_CHANNEL_SWITCH;
if (peer_mac)
ether_addr_copy(p->da, peer_mac);
p->initiator = 1;
chan_switch.primary_chan = primary_chan;
chan_switch.second_chan_offset = second_chan_offset;
chan_switch.band = band;
p->paylen = sizeof(chan_switch);
memcpy(p->payload, &chan_switch, p->paylen);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_cancel_tdls_channel_switch(struct sprdwl_priv *priv, u8 vif_ctx_id,
const u8 *peer_mac)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_tdls *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TDLS);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_tdls *)msg->data;
p->tdls_sub_cmd_mgmt = SPRDWL_TDLS_CANCEL_CHANNEL_SWITCH;
if (peer_mac)
ether_addr_copy(p->da, peer_mac);
p->initiator = 1;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_notify_ip(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 ip_type,
u8 *ip_addr)
{
struct sprdwl_msg_buf *msg;
u8 *ip_value;
u8 ip_len;
if (ip_type != SPRDWL_IPV4 && ip_type != SPRDWL_IPV6)
return -EINVAL;
ip_len = (ip_type == SPRDWL_IPV4) ?
SPRDWL_IPV4_ADDR_LEN : SPRDWL_IPV6_ADDR_LEN;
msg = sprdwl_cmd_getbuf(priv, ip_len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_NOTIFY_IP_ACQUIRED);
if (!msg)
return -ENOMEM;
ip_value = (unsigned char *)msg->data;
memcpy(ip_value, ip_addr, ip_len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_blacklist(struct sprdwl_priv *priv,
u8 vif_ctx_id, u8 sub_type, u8 num, u8 *mac_addr)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_blacklist *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + num * ETH_ALEN,
vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_SET_BLACKLIST);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_blacklist *)msg->data;
p->sub_type = sub_type;
p->num = num;
if (mac_addr)
memcpy(p->mac, mac_addr, num * ETH_ALEN);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_whitelist(struct sprdwl_priv *priv, u8 vif_ctx_id,
u8 sub_type, u8 num, u8 *mac_addr)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_mac_addr *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + num * ETH_ALEN,
vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_SET_WHITELIST);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_set_mac_addr *)msg->data;
p->sub_type = sub_type;
p->num = num;
if (mac_addr)
memcpy(p->mac, mac_addr, num * ETH_ALEN);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_mc_filter(struct sprdwl_priv *priv, u8 vif_ctx_id,
u8 sub_type, u8 num, u8 *mac_addr)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_mac_addr *p;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + num * ETH_ALEN,
vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_MULTICAST_FILTER);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_set_mac_addr *)msg->data;
p->sub_type = sub_type;
p->num = num;
if (num && mac_addr)
memcpy(p->mac, mac_addr, num * ETH_ALEN);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_npi_send_recv(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 *s_buf,
u16 s_len, u8 *r_buf, u16 *r_len)
{
struct sprdwl_msg_buf *msg;
msg = sprdwl_cmd_getbuf(priv, s_len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_NPI_MSG);
if (!msg)
return -ENOMEM;
memcpy(msg->data, s_buf, s_len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, r_len);
}
int sprdwl_set_11v_feature_support(struct sprdwl_priv *priv,
u8 vif_ctx_id, u16 val)
{
struct sprdwl_msg_buf *msg = NULL;
struct sprdwl_cmd_rsp_state_code state;
struct sprdwl_cmd_11v *p = NULL;
u16 rlen = sizeof(state);
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, 1, WIFI_CMD_11V);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_11v *)msg->data;
p->cmd = SPRDWL_SUBCMD_SET;
p->value = (val << 16) | val;
/* len only 8 = cmd(2) + len(2) +value(4)*/
p->len = 8;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT,
(u8 *)&state, &rlen);
}
int sprdwl_set_11v_sleep_mode(struct sprdwl_priv *priv, u8 vif_ctx_id,
u8 status, u16 interval)
{
struct sprdwl_msg_buf *msg = NULL;
struct sprdwl_cmd_rsp_state_code state;
struct sprdwl_cmd_11v *p = NULL;
u16 rlen = sizeof(state);
u32 value = 0;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id, 1, WIFI_CMD_11V);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_11v *)msg->data;
p->cmd = SPRDWL_SUBCMD_ENABLE;
/* 24-31 feature 16-23 status 0-15 interval */
value = SPRDWL_11V_SLEEP << 8;
value = (value | status) << 16;
value = value | interval;
p->value = value;
/* len only 8 = cmd(2) + len(2) +value(4)*/
p->len = 8;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT,
(u8 *)&state, &rlen);
}
int sprdwl_send_ba_mgmt(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 len)
{
struct sprdwl_msg_buf *msg = NULL;
msg = sprdwl_cmd_getbuf(priv, sizeof(struct sprdwl_cmd_ba),
vif_ctx_id, 1, WIFI_CMD_BA);
if (!msg)
return -ENOMEM;
memcpy(msg->data, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_set_max_clients_allowed(struct sprdwl_priv *priv,
u8 vif_ctx_id, int n_clients)
{
int *max;
struct sprdwl_msg_buf *msg;
msg = sprdwl_cmd_getbuf(priv, sizeof(*max), vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_SET_MAX_CLIENTS_ALLOWED);
if (!msg)
return -ENOMEM;
*(int *)msg->data = n_clients;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
void sprdwl_add_hang_cmd(struct sprdwl_vif *vif)
{
struct sprdwl_work *misc_work;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(vif->priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
if (sprdwl_intf_is_exit(vif->priv)
|| (tx_msg->hang_recovery_status == HANG_RECOVERY_ACKED
&& cmd->cmd_id != WIFI_CMD_HANG_RECEIVED)) {
complete(&cmd->completed);
}
misc_work = sprdwl_alloc_work(0);
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_HANG_RECEIVED;
sprdwl_queue_work(vif->priv, misc_work);
}
void sprdwl_add_close_cmd(struct sprdwl_vif *vif, enum sprdwl_mode mode)
{
struct sprdwl_work *misc_work;
misc_work = sprdwl_alloc_work(1);
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_SEND_CLOSE;
memcpy(misc_work->data, &mode, 1);
sprdwl_queue_work(vif->priv, misc_work);
}
/* CP2 send WIFI_EVENT_HANG_RECOVERY to Driver,
* then Driver need to send a WIFI_CMD_HANG_RECEIVED cmd to CP2
* to notify that CP2 can reset credit now.
*/
int sprdwl_send_hang_received_cmd(struct sprdwl_priv *priv, u8 vif_ctx_id)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
msg = sprdwl_cmd_getbuf(priv, 0, vif_ctx_id, SPRDWL_HEAD_RSP,
WIFI_CMD_HANG_RECEIVED);
if (!msg)
return -ENOMEM;
tx_msg->hang_recovery_status = HANG_RECOVERY_ACKED;
return sprdwl_cmd_send_recv(priv, msg,
CMD_WAIT_TIMEOUT, NULL, NULL);
}
void sprdwl_send_assert_cmd(struct sprdwl_vif *vif, u8 cmd_id, u8 reason)
{
struct sprdwl_work *misc_work;
struct sprdwl_assert_info *assert_info;
misc_work = sprdwl_alloc_work(sizeof(struct sprdwl_assert_info));
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_ASSERT;
assert_info = (struct sprdwl_assert_info *)(misc_work->data);
assert_info->cmd_id = cmd_id;
assert_info->reason = reason;
sprdwl_queue_work(vif->priv, misc_work);
}
/* add a reason to CMD assert
* 0:scan time out
* 1:rsp cnt lost
*/
int wlan_set_assert(struct sprdwl_priv *priv, u8 vif_ctx_id, u8 cmd_id, u8 reason)
{
#ifndef ATCMD_ASSERT
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_set_assert *p;
return 0;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_ERR);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_set_assert *)msg->data;
p->reason = reason;
return sprdwl_cmd_send_recv(priv, msg,
CMD_WAIT_TIMEOUT, NULL, NULL);
#else
return sprdwl_atcmd_assert(priv, vif_ctx_id, cmd_id, reason);
#endif
}
int sprdwl_send_data2cmd(struct sprdwl_priv *priv, u8 vif_ctx_id,
void *data, u16 len)
{
struct sprdwl_msg_buf *msg = NULL;
msg = sprdwl_cmd_getbuf(priv, len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TX_DATA);
if (!msg)
return -ENOMEM;
memcpy(msg->data, data, len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_xmit_data2cmd(struct sk_buff *skb, struct net_device *ndev)
{
#define FLAG_SIZE 5
struct sprdwl_vif *vif = netdev_priv(ndev);
struct sprdwl_msg_buf *msg;
u8 *temp_flag = "01234";
struct tx_msdu_dscr *dscr;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
if (unlikely(atomic_read(&cmd->refcnt) > 0)) {
wl_err("%s, cmd->refcnt = %d, Try later again\n",
__func__, atomic_read(&cmd->refcnt));
return -EAGAIN;
}
if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
u8 *data = (u8 *)(skb->data) + sizeof(struct ethhdr);
struct sprdwl_eap_hdr *eap = (struct sprdwl_eap_hdr *)data;
if (eap->type == EAP_PACKET_TYPE && eap->opcode == EAP_WSC_DONE) {
wl_info("%s, EAP_WSC_DONE!\n", __func__);
vif->wps_flag = 1;
}
}
/*fill dscr header first*/
if (sprdwl_intf_fill_msdu_dscr(vif, skb, SPRDWL_TYPE_CMD, 0)) {
dev_kfree_skb(skb);
return -EPERM;
}
/*alloc five byte for fw 16 byte need
*dscr:11+flag:5 =16
*/
skb_push(skb, FLAG_SIZE);
memcpy(skb->data, temp_flag, FLAG_SIZE);
/*malloc msg buffer*/
msg = sprdwl_cmd_getbuf_atomic(vif->priv, skb->len, vif->ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TX_DATA);
if (!msg) {
wl_err("%s, %d, getmsgbuf fail, free skb\n",
__func__, __LINE__);
dev_kfree_skb(skb);
return -ENOMEM;
}
/*send group in BK to avoid FW hang*/
dscr = (struct tx_msdu_dscr *)skb->data;
if ((vif->mode == SPRDWL_MODE_AP ||
vif->mode == SPRDWL_MODE_P2P_GO) &&
(dscr->sta_lut_index < 6)) {
dscr->buffer_info.msdu_tid = prio_1;
wl_info("%s, %d, SOFTAP/GO group go as BK\n", __func__, __LINE__);
}
memcpy(msg->data, skb->data, skb->len);
dev_kfree_skb(skb);
return sprdwl_cmd_send_to_ic(vif->priv, msg);
}
int sprdwl_xmit_data2cmd_wq(struct sk_buff *skb, struct net_device *ndev)
{
#define FLAG_SIZE 5
struct sprdwl_vif *vif = netdev_priv(ndev);
u8 *temp_flag = "01234";
struct tx_msdu_dscr *dscr;
struct sprdwl_work *misc_work = NULL;
/*fill dscr header first*/
if (sprdwl_intf_fill_msdu_dscr(vif, skb, SPRDWL_TYPE_CMD, 0)) {
dev_kfree_skb(skb);
return -EPERM;
}
/*alloc five byte for fw 16 byte need
*dscr:11+flag:5 =16
*/
skb_push(skb, FLAG_SIZE);
memcpy(skb->data, temp_flag, FLAG_SIZE),
/*send group in BK to avoid FW hang*/
dscr = (struct tx_msdu_dscr *)skb->data;
if ((vif->mode == SPRDWL_MODE_AP ||
vif->mode == SPRDWL_MODE_P2P_GO) &&
(dscr->sta_lut_index < 6)) {
dscr->buffer_info.msdu_tid = prio_1;
wl_info("%s, %d, SOFTAP/GO group go as BK\n", __func__, __LINE__);
}
/*create work queue*/
misc_work = sprdwl_alloc_work(skb->len);
if (!misc_work) {
wl_err("%s:work queue alloc failure\n", __func__);
dev_kfree_skb(skb);
return -1;
}
memcpy(misc_work->data, skb->data, skb->len);
dev_kfree_skb(skb);
misc_work->vif = vif;
misc_work->id = SPRDWL_CMD_TX_DATA;
sprdwl_queue_work(vif->priv, misc_work);
return 0;
}
int sprdwl_send_vowifi_data_prot(struct sprdwl_priv *priv, u8 ctx_id,
void *data, int len)
{
struct sprdwl_msg_buf *msg;
wl_info("enter--at %s\n", __func__);
if (priv == NULL)
return -EINVAL;
msg = sprdwl_cmd_getbuf(priv, 0, ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_VOWIFI_DATA_PROTECT);
if (!msg)
return -ENOMEM;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
void sprdwl_vowifi_data_protection(struct sprdwl_vif *vif)
{
struct sprdwl_work *misc_work;
wl_info("enter--at %s\n", __func__);
misc_work = sprdwl_alloc_work(0);
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_WORK_VOWIFI_DATA_PROTECTION;
sprdwl_queue_work(vif->priv, misc_work);
}
void sprdwl_work_host_wakeup_fw(struct sprdwl_vif *vif)
{
struct sprdwl_work *misc_work;
misc_work = sprdwl_alloc_work(0);
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return;
}
if (!vif) {
wl_err("%s vif is null!\n", __func__);
return;
}
if (!vif->priv) {
wl_err("%s priv is null!\n", __func__);
return;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_WORK_HOST_WAKEUP_FW;
sprdwl_queue_work(vif->priv, misc_work);
}
int sprdwl_cmd_host_wakeup_fw(struct sprdwl_priv *priv, u8 ctx_id)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_power_save *p;
u8 r_buf = -1;
u16 r_len = 1;
int ret = 0;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_POWER_SAVE);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_power_save *)msg->data;
p->sub_type = SPRDWL_HOST_WAKEUP_FW;
p->value = 0;
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT,
&r_buf, &r_len);
if (!ret && (1 == r_buf)) {
intf->fw_awake = 1;
tx_up(tx_msg);
} else {
intf->fw_awake = 0;
intf->fw_power_down = 1;
tx_up(tx_msg);
wl_err("host wakeup fw cmd failed, ret=%d\n", ret);
}
return ret;
}
int sprdwl_cmd_req_lte_concur(struct sprdwl_priv *priv, u8 ctx_id, u8 user_channel)
{
struct sprdwl_msg_buf *msg;
msg = sprdwl_cmd_getbuf(priv, sizeof(user_channel), ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_REQ_LTE_CONCUR);
if (!msg)
return -ENOMEM;
*(u8 *)msg->data = user_channel;
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
static int handle_rsp_status_err(u8 cmd_id, s8 status)
{
int flag = 0;
switch (cmd_id) {
case WIFI_CMD_DOWNLOAD_INI:
if ((SPRDWL_CMD_STATUS_CRC_ERROR == status) ||
(SPRDWL_CMD_STATUS_INI_INDEX_ERROR == status) ||
(SPRDWL_CMD_STATUS_LENGTH_ERROR == status))
flag = -1;
break;
case WIFI_CMD_OPEN:
if (SPRDWL_CMD_STATUS_ARG_ERROR == status)
flag = -1;
break;
default:
flag = 0;
break;
}
return flag;
}
/* retrun the msg length or 0 */
unsigned short sprdwl_rx_rsp_process(struct sprdwl_priv *priv, u8 *msg)
{
u16 plen;
void *data;
int handle_flag = 0;
struct sprdwl_cmd *cmd = &g_sprdwl_cmd;
struct sprdwl_cmd_hdr *hdr;
if (unlikely(!cmd->init_ok)) {
wl_info("%s cmd coming too early, drop it\n", __func__);
return 0;
}
hdr = (struct sprdwl_cmd_hdr *)msg;
plen = SPRDWL_GET_LE16(hdr->plen);
/* 2048 use mac */
/*TODO here ctx_id range*/
#ifndef CONFIG_P2P_INTF
if (hdr->common.ctx_id > STAP_MODE_P2P_DEVICE ||
#else
if (hdr->common.ctx_id >= STAP_MODE_COEXI_NUM ||
#endif
hdr->cmd_id > WIFI_CMD_MAX ||
plen > 2048) {
wl_err("%s wrong CMD_RSP: ctx_id:%d;cmd_id:%d\n",
__func__, hdr->common.ctx_id,
hdr->cmd_id);
return 0;
}
if (atomic_inc_return(&cmd->refcnt) >= SPRDWL_CMD_EXIT_VAL) {
atomic_dec(&cmd->refcnt);
wl_err("cmd->refcnt=%x\n", atomic_read(&cmd->refcnt));
return 0;
}
data = kmalloc(plen, GFP_KERNEL);
if (!data) {
atomic_dec(&cmd->refcnt);
wl_err("cmd->refcnt=%x\n", atomic_read(&cmd->refcnt));
return plen;
}
memcpy(data, (void *)hdr, plen);
spin_lock_bh(&cmd->lock);
if (!cmd->data && SPRDWL_GET_LE32(hdr->mstime) == cmd->mstime &&
hdr->cmd_id == cmd->cmd_id) {
wl_debug("ctx_id %d recv rsp[%s]\n",
hdr->common.ctx_id, cmd2str(hdr->cmd_id));
if (unlikely(hdr->status != 0)) {
wl_err("%s ctx_id %d recv rsp[%s] status[%s]\n",
__func__, hdr->common.ctx_id,
cmd2str(hdr->cmd_id),
err2str(hdr->status));
handle_flag = handle_rsp_status_err(hdr->cmd_id,
hdr->status);
}
cmd->data = data;
complete(&cmd->completed);
} else {
kfree(data);
wl_err("%s ctx_id %d recv mismatched rsp[%s] status[%s]\n",
__func__, hdr->common.ctx_id,
cmd2str(hdr->cmd_id),
err2str(hdr->status));
wl_err("%s mstime:[%u %u]\n", __func__,
SPRDWL_GET_LE32(hdr->mstime), cmd->mstime);
}
spin_unlock_bh(&cmd->lock);
atomic_dec(&cmd->refcnt);
wl_debug("cmd->refcnt=%x\n", atomic_read(&cmd->refcnt));
if (0 != handle_flag)
wlan_set_assert(priv, SPRDWL_MODE_NONE, hdr->cmd_id, HANDLE_FLAG_ERROR);
return plen;
}
/* Events */
void sprdwl_event_station(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_event_new_station *sta =
(struct sprdwl_event_new_station *)data;
sprdwl_report_softap(vif, sta->is_connect,
sta->mac, sta->ie, sta->ie_len);
}
void sprdwl_event_scan_done(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_event_scan_done *p =
(struct sprdwl_event_scan_done *)data;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
u8 bucket_id = 0;
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
switch (p->type) {
case SPRDWL_SCAN_DONE:
sprdwl_scan_done(vif, false);
wl_ndev_log(L_DBG, vif->ndev, "%s got %d BSSes\n", __func__,
bss_count);
break;
case SPRDWL_SCHED_SCAN_DONE:
sprdwl_sched_scan_done(vif, false);
wl_ndev_log(L_DBG, vif->ndev, "%s schedule scan got %d BSSes\n",
__func__, bss_count);
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
case SPRDWL_GSCAN_DONE:
bucket_id = ((struct sprdwl_event_gscan_done *)data)->bucket_id;
sprdwl_gscan_done(vif, bucket_id);
wl_ndev_log(L_DBG, vif->ndev, "%s gscan got %d bucketid done\n",
__func__, bucket_id);
break;
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
case SPRDWL_SCAN_ERROR:
default:
sprdwl_scan_done(vif, true);
sprdwl_sched_scan_done(vif, false);
if (p->type == SPRDWL_SCAN_ERROR)
wl_ndev_log(L_ERR, vif->ndev, "%s error!\n", __func__);
else
wl_ndev_log(L_ERR, vif->ndev, "%s invalid scan done type: %d\n",
__func__, p->type);
break;
}
bss_count = 0;
}
void sprdwl_event_connect(struct sprdwl_vif *vif, u8 *data, u16 len)
{
u8 *pos = data;
u8 status_code = 0;
int left = len;
struct sprdwl_connect_info conn_info;
#ifdef COMPAT_SAMPILE_CODE
u8 compat_ver = 0;
struct sprdwl_priv *priv = vif->priv;
compat_ver = need_compat_operation(priv, WIFI_EVENT_CONNECT);
if (compat_ver) {
switch (compat_ver) {
case VERSION_1:
/*add data struct modification in here!*/
break;
case VERSION_2:
/*add data struct modification in here!*/
break;
case VERSION_3:
/*add data struct modification in here!*/
break;
default:
break;
}
}
#endif
/*init data struct*/
memset(&conn_info, 0, sizeof(conn_info));
/* the first byte is status code */
memcpy(&conn_info.status, pos, sizeof(conn_info.status));
if (conn_info.status != SPRDWL_CONNECT_SUCCESS &&
conn_info.status != SPRDWL_ROAM_SUCCESS){
/*Assoc response status code by set in the 3 byte if failure*/
memcpy(&status_code, pos+2, sizeof(status_code));
goto out;
}
pos += sizeof(conn_info.status);
left -= sizeof(conn_info.status);
/* parse BSSID */
if (left < ETH_ALEN)
goto out;
conn_info.bssid = pos;
pos += ETH_ALEN;
left -= ETH_ALEN;
/* get channel */
if (left < sizeof(conn_info.channel))
goto out;
memcpy(&conn_info.channel, pos, sizeof(conn_info.channel));
pos += sizeof(conn_info.channel);
left -= sizeof(conn_info.channel);
/* get signal */
if (left < sizeof(conn_info.signal))
goto out;
memcpy(&conn_info.signal, pos, sizeof(conn_info.signal));
pos += sizeof(conn_info.signal);
left -= sizeof(conn_info.signal);
/* parse REQ IE */
if (left < 0)
goto out;
memcpy(&conn_info.req_ie_len, pos, sizeof(conn_info.req_ie_len));
pos += sizeof(conn_info.req_ie_len);
left -= sizeof(conn_info.req_ie_len);
conn_info.req_ie = pos;
pos += conn_info.req_ie_len;
left -= conn_info.req_ie_len;
/* parse RESP IE */
if (left < 0)
goto out;
memcpy(&conn_info.resp_ie_len, pos, sizeof(conn_info.resp_ie_len));
pos += sizeof(conn_info.resp_ie_len);
left -= sizeof(conn_info.resp_ie_len);
conn_info.resp_ie = pos;
pos += conn_info.resp_ie_len;
left -= conn_info.resp_ie_len;
/* parse BEA IE */
if (left < 0)
goto out;
memcpy(&conn_info.bea_ie_len, pos, sizeof(conn_info.bea_ie_len));
pos += sizeof(conn_info.bea_ie_len);
left -= sizeof(conn_info.bea_ie_len);
conn_info.bea_ie = pos;
out:
sprdwl_report_connection(vif, &conn_info, status_code);
}
void sprdwl_event_disconnect(struct sprdwl_vif *vif, u8 *data, u16 len)
{
u16 reason_code;
memcpy(&reason_code, data, sizeof(reason_code));
wl_info("%s reason code = %d\n", __func__, reason_code);
complete(&vif->disconnect_completed);
sprdwl_report_disconnection(vif, reason_code);
}
void sprdwl_event_mic_failure(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_event_mic_failure *mic_failure =
(struct sprdwl_event_mic_failure *)data;
sprdwl_report_mic_failure(vif, mic_failure->is_mcast,
mic_failure->key_id);
}
void sprdwl_event_remain_on_channel_expired(struct sprdwl_vif *vif,
u8 *data, u16 len)
{
sprdwl_report_remain_on_channel_expired(vif);
}
void sprdwl_event_mlme_tx_status(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_event_mgmt_tx_status *tx_status =
(struct sprdwl_event_mgmt_tx_status *)data;
sprdwl_report_mgmt_tx_status(vif, SPRDWL_GET_LE64(tx_status->cookie),
tx_status->buf,
SPRDWL_GET_LE16(tx_status->len),
tx_status->ack);
}
/* @flag: 1 for data, 0 for event */
void sprdwl_event_frame(struct sprdwl_vif *vif, u8 *data, u16 len, int flag)
{
struct sprdwl_event_mgmt_frame *frame;
u16 buf_len;
u8 *buf = NULL;
u8 channel, type;
if (flag) {
/* here frame maybe not 4 bytes align */
frame = (struct sprdwl_event_mgmt_frame *)
(data - sizeof(*frame) + len);
buf = data - sizeof(*frame);
} else {
frame = (struct sprdwl_event_mgmt_frame *)data;
buf = frame->data;
}
channel = frame->channel;
type = frame->type;
buf_len = SPRDWL_GET_LE16(frame->len);
sprdwl_cfg80211_dump_frame_prot_info(0, 0, buf, buf_len);
switch (type) {
case SPRDWL_FRAME_NORMAL:
sprdwl_report_rx_mgmt(vif, channel, buf, buf_len);
break;
case SPRDWL_FRAME_DEAUTH:
sprdwl_report_mgmt_deauth(vif, buf, buf_len);
break;
case SPRDWL_FRAME_DISASSOC:
sprdwl_report_mgmt_disassoc(vif, buf, buf_len);
break;
case SPRDWL_FRAME_SCAN:
sprdwl_report_scan_result(vif, channel, frame->signal,
buf, buf_len);
++bss_count;
break;
default:
wl_ndev_log(L_ERR, vif->ndev, "%s invalid frame type: %d!\n",
__func__, type);
break;
}
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
void sprdwl_event_epno_results(struct sprdwl_vif *vif, u8 *data, u16 data_len)
{
int i;
u64 msecs, now;
struct sk_buff *skb;
struct nlattr *attr, *sub_attr;
struct sprdwl_epno_results *epno_results;
struct sprdwl_priv *priv = vif->priv;
struct wiphy *wiphy = priv->wiphy;
wl_hex_dump(L_DBG, "epno result:", DUMP_PREFIX_OFFSET,
16, 1, data, data_len, true);
epno_results = (struct sprdwl_epno_results *)data;
if (epno_results->nr_scan_results <= 0) {
wl_err("%s invalid data\n", __func__);
return;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 83)
skb = cfg80211_vendor_event_alloc(wiphy, &vif->wdev, data_len,
#else
skb = cfg80211_vendor_event_alloc(wiphy, data_len,
#endif
SPRDWL_VENDOR_EVENT_EPNO_FOUND,
GFP_KERNEL);
if (skb == NULL) {
wl_ndev_log(L_ERR, vif->ndev, "skb alloc failed");
return;
}
nla_put_u32(skb, GSCAN_RESULTS_REQUEST_ID, epno_results->request_id);
nla_put_u32(skb, GSCAN_RESULTS_NUM_RESULTS_AVAILABLE,
epno_results->nr_scan_results);
nla_put_u8(skb, GSCAN_RESULTS_SCAN_RESULT_MORE_DATA, 0);
attr = nla_nest_start(skb, GSCAN_RESULTS_LIST);
if (attr == NULL)
goto failed;
now = jiffies;
if (now > epno_results->boot_time)
msecs = jiffies_to_msecs(now - epno_results->boot_time);
else {
now += (MAX_JIFFY_OFFSET - epno_results->boot_time) + 1;
msecs = jiffies_to_msecs(now);
}
for (i = 0; i < epno_results->nr_scan_results; i++) {
sub_attr = nla_nest_start(skb, i);
if (sub_attr == NULL)
goto failed;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
nla_put_u64_64bit(skb, GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, msecs, 0);
#else
nla_put_u64(skb, GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP, msecs);
#endif
nla_put(skb, GSCAN_RESULTS_SCAN_RESULT_BSSID, ETH_ALEN,
epno_results->results[i].bssid);
nla_put_u32(skb, GSCAN_RESULTS_SCAN_RESULT_CHANNEL,
epno_results->results[i].channel);
nla_put_s32(skb, GSCAN_RESULTS_SCAN_RESULT_RSSI,
epno_results->results[i].rssi);
nla_put_u32(skb, GSCAN_RESULTS_SCAN_RESULT_RTT,
epno_results->results[i].rtt);
nla_put_u32(skb, GSCAN_RESULTS_SCAN_RESULT_RTT_SD,
epno_results->results[i].rtt_sd);
nla_put_u16(skb, GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD,
epno_results->results[i].beacon_period);
nla_put_u16(skb, GSCAN_RESULTS_SCAN_RESULT_CAPABILITY,
epno_results->results[i].capability);
nla_put_string(skb, GSCAN_RESULTS_SCAN_RESULT_SSID,
epno_results->results[i].ssid);
nla_nest_end(skb, sub_attr);
}
nla_nest_end(skb, attr);
cfg80211_vendor_event(skb, GFP_KERNEL);
wl_debug("report epno event success, count = %d\n",
epno_results->nr_scan_results);
return;
failed:
kfree(skb);
wl_err("%s report epno event failed\n", __func__);
}
void sprdwl_event_gscan_frame(struct sprdwl_vif *vif, u8 *data, u16 len)
{
u32 report_event;
u8 *pos = data;
s32 avail_len = len;
struct sprdwl_gscan_result *frame;
u16 buf_len;
u8 bucket_id = 0;
report_event = *(u32 *)pos;
avail_len -= sizeof(u32);
pos += sizeof(u32);
if (report_event & REPORT_EVENTS_EPNO)
return sprdwl_event_epno_results(vif, pos, avail_len);
/*significant change result is different with gsan with, deal it specially*/
if (report_event & REPORT_EVENTS_SIGNIFICANT_CHANGE) {
sprdwl_vendor_cache_significant_change_result(vif,
pos, avail_len);
sprdwl_significant_change_event(vif);
return;
}
while (avail_len > 0) {
if (avail_len < sizeof(struct sprdwl_gscan_result)) {
wl_ndev_log(L_ERR, vif->ndev,
"%s invalid available length: %d!\n",
__func__, avail_len);
break;
}
bucket_id = *(u8 *)pos;
pos += sizeof(u8);
frame = (struct sprdwl_gscan_result *)pos;
frame->ts = jiffies;
buf_len = frame->ie_length;
if ((report_event == REPORT_EVENTS_BUFFER_FULL) ||
(report_event & REPORT_EVENTS_EACH_SCAN))
sprdwl_vendor_cache_scan_result(vif, bucket_id, frame);
else if (report_event & REPORT_EVENTS_FULL_RESULTS)
sprdwl_vendor_report_full_scan(vif, frame);
else if (report_event & REPORT_EVENTS_HOTLIST_RESULTS_FOUND ||
report_event & REPORT_EVENTS_HOTLIST_RESULTS_LOST) {
sprdwl_vendor_cache_hotlist_result(vif, frame);
}
avail_len -= sizeof(struct sprdwl_gscan_result) + buf_len + 1;
pos += sizeof(struct sprdwl_gscan_result) + buf_len;
wl_ndev_log(L_DBG, vif->ndev, "%s ch:%d id:%d len:%d aval:%d, report_event:%d\n",
__func__, frame->channel, bucket_id,
buf_len, avail_len, report_event);
}
if (report_event == REPORT_EVENTS_BUFFER_FULL)
sprdwl_buffer_full_event(vif);
else if (report_event & REPORT_EVENTS_EACH_SCAN)
sprdwl_gscan_done(vif, bucket_id);
else if (report_event & REPORT_EVENTS_HOTLIST_RESULTS_FOUND ||
report_event & REPORT_EVENTS_HOTLIST_RESULTS_LOST) {
sprdwl_hotlist_change_event(vif, report_event);
}
}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
void sprdwl_event_cqm(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_event_cqm *p;
u8 rssi_event;
p = (struct sprdwl_event_cqm *)data;
switch (p->status) {
case SPRDWL_CQM_RSSI_LOW:
rssi_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
break;
case SPRDWL_CQM_RSSI_HIGH:
rssi_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
break;
case SPRDWL_CQM_BEACON_LOSS:
/* TODO wpa_supplicant not support the event ,
* so we workaround this issue
*/
rssi_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
vif->beacon_loss = 1;
break;
default:
wl_ndev_log(L_ERR, vif->ndev, "%s invalid event!\n", __func__);
return;
}
sprdwl_report_cqm(vif, rssi_event);
}
void sprdwl_event_tdls(struct sprdwl_vif *vif, u8 *data, u16 len)
{
unsigned char peer[ETH_ALEN];
u8 oper;
u16 reason_code;
struct sprdwl_event_tdls *report_tdls = NULL;
report_tdls = (struct sprdwl_event_tdls *)data;
ether_addr_copy(&peer[0], &report_tdls->mac[0]);
oper = report_tdls->tdls_sub_cmd_mgmt;
if (SPRDWL_TDLS_TEARDOWN == oper)
oper = NL80211_TDLS_TEARDOWN;
else if (SPRDWL_TDLS_UPDATE_PEER_INFOR == oper)
sprdwl_event_tdls_flow_count(vif, data, len);
else {
oper = NL80211_TDLS_SETUP;
sprdwl_tdls_flow_flush(vif, peer, oper);
}
reason_code = 0;
sprdwl_report_tdls(vif, peer, oper, reason_code);
}
int sprdwl_send_tdlsdata_use_cmd(struct sk_buff *skb,
struct sprdwl_vif *vif, u8 need_cp2_rsp)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_tdls *p;
struct sprdwl_intf *intf;
intf = (struct sprdwl_intf *)(vif->priv->hw_priv);
msg = sprdwl_cmd_getbuf(vif->priv, sizeof(*p) + skb->len, vif->ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_TDLS);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_tdls *)msg->data;
p->tdls_sub_cmd_mgmt = WLAN_TDLS_CMD_TX_DATA;
ether_addr_copy(p->da, skb->data);
p->paylen = skb->len;/*TBD*/
memcpy(p->payload, skb->data, skb->len);
if (need_cp2_rsp)
return sprdwl_cmd_send_recv(vif->priv, msg,
CMD_WAIT_TIMEOUT, NULL, NULL);
else
return sprdwl_cmd_send_recv_no_wait(vif->priv, msg);
}
inline void sprdwl_event_ba_mgmt(struct sprdwl_vif *vif, u8 *data, u16 len)
{
wlan_ba_session_event(vif->priv->hw_priv, data, len);
}
void sprdwl_event_suspend_resume(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_event_suspend_resume *suspend_resume = NULL;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(vif->priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
suspend_resume = (struct sprdwl_event_suspend_resume *)data;
if ((1 == suspend_resume->status) &&
(intf->suspend_mode == SPRDWL_PS_RESUMING)) {
intf->suspend_mode = SPRDWL_PS_RESUMED;
tx_up(tx_msg);
wl_info("%s, %d,resumed,wakeuptx\n", __func__, __LINE__);
}
}
void sprdwl_event_hang_recovery(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct event_hang_recovery *hang =
(struct event_hang_recovery *)data;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(vif->priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
tx_msg->hang_recovery_status = hang->action;
wl_info("%s, %d, action=%d, status=%d\n",
__func__, __LINE__,
hang->action,
tx_msg->hang_recovery_status);
if (hang->action == HANG_RECOVERY_BEGIN)
sprdwl_add_hang_cmd(vif);
if (hang->action == HANG_RECOVERY_END)
tx_up(tx_msg);
}
void sprdwl_event_thermal_warn(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct event_thermal_warn *thermal =
(struct event_thermal_warn *)data;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(vif->priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
enum sprdwl_mode mode;
wl_info("%s, %d, action=%d, status=%d\n",
__func__, __LINE__,
thermal->action,
tx_msg->thermal_status);
if (tx_msg->thermal_status == THERMAL_WIFI_DOWN)
return;
tx_msg->thermal_status = thermal->action;
switch (thermal->action) {
case THERMAL_TX_RESUME:
sprdwl_net_flowcontrl(intf->priv, SPRDWL_MODE_NONE, true);
tx_up(tx_msg);
break;
case THERMAL_TX_STOP:
wl_err("%s, %d, netif_stop_queue because of thermal warn\n",
__func__, __LINE__);
sprdwl_net_flowcontrl(intf->priv, SPRDWL_MODE_NONE, false);
break;
case THERMAL_WIFI_DOWN:
wl_err("%s, %d, close wifi because of thermal warn\n",
__func__, __LINE__);
sprdwl_net_flowcontrl(intf->priv, SPRDWL_MODE_NONE, false);
for (mode = SPRDWL_MODE_STATION; mode < SPRDWL_MODE_MAX; mode++) {
if (intf->priv->fw_stat[mode] == SPRDWL_INTF_OPEN)
sprdwl_add_close_cmd(vif, mode);
}
break;
default:
break;
}
}
#define WIFI_EVENT_WFD_RATE 0x30
extern int wfd_notifier_call_chain(unsigned long val, void *v);
void sprdwl_wfd_mib_cnt(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct event_wfd_mib_cnt *wfd =
(struct event_wfd_mib_cnt *)data;
u32 tx_cnt, busy_cnt, wfd_rate = 0;
wl_info("%s, %d, tp=%d, sum_tp=%d, drop=%d,%d,%d,%d, frame=%d, clear=%d, mib=%d\n",
__func__, __LINE__,
wfd->wfd_throughput, wfd->sum_tx_throughput,
wfd->tx_mpdu_lost_cnt[0], wfd->tx_mpdu_lost_cnt[1], wfd->tx_mpdu_lost_cnt[2], wfd->tx_mpdu_lost_cnt[3],
wfd->tx_frame_cnt, wfd->rx_clear_cnt, wfd->mib_cycle_cnt);
if (!wfd->mib_cycle_cnt)
return;
tx_cnt = wfd->tx_frame_cnt / wfd->mib_cycle_cnt;
busy_cnt = (10 * wfd->rx_clear_cnt) / wfd->mib_cycle_cnt;
if (busy_cnt > 8)
wfd_rate = wfd->sum_tx_throughput;
else{
if (tx_cnt)
wfd_rate = wfd->sum_tx_throughput + wfd->sum_tx_throughput * (1 / tx_cnt) * ((10 - busy_cnt) / 10) / 2;
}
wl_info("%s, %d, wfd_rate=%d\n", __func__, __LINE__, wfd_rate);
wfd_rate = 2;
/* wfd_notifier_call_chain(WIFI_EVENT_WFD_RATE, (void *)(unsigned long)wfd_rate); */
}
int sprdwl_fw_power_down_ack(struct sprdwl_priv *priv, u8 ctx_id)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_power_save *p;
int ret = 0;
struct sprdwl_intf *intf = (struct sprdwl_intf *)(priv->hw_priv);
struct sprdwl_tx_msg *tx_msg = (struct sprdwl_tx_msg *)intf->sprdwl_tx;
enum sprdwl_mode mode = SPRDWL_MODE_NONE;
int tx_num = 0;
msg = sprdwl_cmd_getbuf(priv, sizeof(*p), ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_POWER_SAVE);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_power_save *)msg->data;
p->sub_type = SPRDWL_FW_PWR_DOWN_ACK;
for (mode = SPRDWL_MODE_NONE; mode < SPRDWL_MODE_MAX; mode++) {
int num = atomic_read(&tx_msg->tx_list[mode]->mode_list_num);
tx_num += num;
}
if (tx_num > 0 ||
!list_empty(&tx_msg->xmit_msg_list.to_send_list) ||
!list_empty(&tx_msg->xmit_msg_list.to_free_list)) {
if (intf->fw_power_down == 1)
goto err;
p->value = 0;
intf->fw_power_down = 0;
intf->fw_awake = 1;
} else {
p->value = 1;
intf->fw_power_down = 1;
intf->fw_awake = 0;
}
wl_info("%s, value=%d, fw_pwr_down=%d, fw_awake=%d, %d, %d, %d, %d\n",
__func__,
p->value,
intf->fw_power_down,
intf->fw_awake,
atomic_read(&tx_msg->tx_list_qos_pool.ref),
tx_num,
list_empty(&tx_msg->xmit_msg_list.to_send_list),
list_empty(&tx_msg->xmit_msg_list.to_free_list));
ret = sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
#ifdef CPUFREQ_UPDATE_SUPPORT
if (intf->fw_power_down == 1)
sprdwl_unboost();
#endif /* CPUFREQ_UPDATE_SUPPORT */
if (ret)
wl_err("host send data cmd failed, ret=%d\n", ret);
return ret;
err:
wl_err("%s donot ack FW_PWR_DOWN twice\n", __func__);
sprdwl_intf_free_msg_buf(priv, msg);
return -1;
}
void sprdwl_event_fw_power_down(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_work *misc_work;
misc_work = sprdwl_alloc_work(0);
if (!misc_work) {
wl_err("%s out of memory\n", __func__);
return;
}
misc_work->vif = vif;
misc_work->id = SPRDWL_WORK_FW_PWR_DOWN;
sprdwl_queue_work(vif->priv, misc_work);
}
void sprdwl_event_chan_changed(struct sprdwl_vif *vif, u8 *data, u16 len)
{
struct sprdwl_chan_changed_info *p = (struct sprdwl_chan_changed_info *)data;
u8 channel;
u16 freq;
struct wiphy *wiphy = vif->wdev.wiphy;
struct ieee80211_channel *ch = NULL;
struct cfg80211_chan_def chandef;
if (p->initiator == 0) {
wl_err("%s, unknowed event!\n", __func__);
} else if (p->initiator == 1) {
channel = p->target_channel;
if (channel > 14)
freq = 5000 + channel*5;
else
freq = 2412 + (channel-1)*5;
if (wiphy)
ch = ieee80211_get_channel(wiphy, freq);
else
wl_err("%s, wiphy is null!\n", __func__);
if (ch) {
/* we will be active on the channel */
cfg80211_chandef_create(&chandef, ch,
NL80211_CHAN_HT20);
cfg80211_ch_switch_notify(vif->ndev, &chandef);
} else
wl_err("%s, ch is null!\n", __func__);
}
}
void sprdwl_event_coex_bt_on_off(u8 *data, u16 len)
{
struct event_coex_mode_changed *coex_bt_on_off =
(struct event_coex_mode_changed *)data;
wl_info("%s, %d, action=%d\n",
__func__, __LINE__,
coex_bt_on_off->action);
set_coex_bt_on_off(coex_bt_on_off->action);
}
#define E2S(x) \
{ \
case x: \
str = #x; \
break; \
}
static const char *evt2str(u8 evt)
{
const char *str = NULL;
switch (evt) {
E2S(WIFI_EVENT_CONNECT)
E2S(WIFI_EVENT_DISCONNECT)
E2S(WIFI_EVENT_SCAN_DONE)
E2S(WIFI_EVENT_MGMT_FRAME)
E2S(WIFI_EVENT_MGMT_TX_STATUS)
E2S(WIFI_EVENT_REMAIN_CHAN_EXPIRED)
E2S(WIFI_EVENT_MIC_FAIL)
E2S(WIFI_EVENT_NEW_STATION)
E2S(WIFI_EVENT_CQM)
E2S(WIFI_EVENT_MEASUREMENT)
E2S(WIFI_EVENT_TDLS)
E2S(WIFI_EVENT_SDIO_SEQ_NUM)
E2S(WIFI_EVENT_SDIO_FLOWCON)
E2S(WIFI_EVENT_BA)
E2S(WIFI_EVENT_RSSI_MONITOR)
E2S(WIFI_EVENT_GSCAN_FRAME)
#ifdef DFS_MASTER
E2S(WIFI_EVENT_RADAR_DETECTED)
#endif
E2S(WIFI_EVENT_STA_LUT_INDEX)
E2S(WIFI_EVENT_SUSPEND_RESUME)
E2S(WIFI_EVENT_NAN)
E2S(WIFI_EVENT_RTT)
E2S(WIFI_EVENT_HANG_RECOVERY)
E2S(WIFI_EVENT_THERMAL_WARN)
E2S(WIFI_EVENT_WFD_MIB_CNT)
E2S(WIFI_EVENT_FW_PWR_DOWN)
default :
return "WIFI_EVENT_UNKNOWN";
}
return str;
}
#undef E2S
/* retrun the msg length or 0 */
unsigned short sprdwl_rx_event_process(struct sprdwl_priv *priv, u8 *msg)
{
struct sprdwl_cmd_hdr *hdr = (struct sprdwl_cmd_hdr *)msg;
struct sprdwl_vif *vif;
u8 ctx_id;
u16 len, plen;
u8 *data;
ctx_id = hdr->common.ctx_id;
/*TODO ctx_id range*/
#ifndef CONFIG_P2P_INTF
if (ctx_id > STAP_MODE_P2P_DEVICE) {
#else
if (ctx_id >= STAP_MODE_COEXI_NUM) {
#endif
wl_info("%s invalid ctx_id: %d\n", __func__, ctx_id);
return 0;
}
plen = SPRDWL_GET_LE16(hdr->plen);
if (!priv) {
wl_err("%s priv is NULL [%u]ctx_id %d recv[%s]len: %d\n",
__func__, le32_to_cpu(hdr->mstime), ctx_id,
evt2str(hdr->cmd_id), hdr->plen);
return plen;
}
wl_debug("[%u]ctx_id %d recv[%s]len: %d\n",
le32_to_cpu(hdr->mstime), ctx_id,
evt2str(hdr->cmd_id), plen);
wl_hex_dump(L_DBG, "EVENT: ", DUMP_PREFIX_OFFSET, 16, 1,
(u8 *)hdr, hdr->plen, 0);
len = plen - sizeof(*hdr);
vif = ctx_id_to_vif(priv, ctx_id);
if (!vif) {
wl_info("%s NULL vif for ctx_id: %d, len:%d\n",
__func__, ctx_id, plen);
return plen;
}
if (!((long)msg & 0x3)) {
data = (u8 *)msg;
data += sizeof(*hdr);
} else {
/* never into here when the dev is BA or MARLIN2,
* temply used as debug and safe
*/
WARN_ON(1);
data = kmalloc(len, GFP_KERNEL);
if (!data) {
sprdwl_put_vif(vif);
return plen;
}
memcpy(data, msg + sizeof(*hdr), len);
}
switch (hdr->cmd_id) {
case WIFI_EVENT_CONNECT:
sprdwl_event_connect(vif, data, len);
break;
case WIFI_EVENT_DISCONNECT:
sprdwl_event_disconnect(vif, data, len);
break;
case WIFI_EVENT_REMAIN_CHAN_EXPIRED:
sprdwl_event_remain_on_channel_expired(vif, data, len);
break;
case WIFI_EVENT_NEW_STATION:
sprdwl_event_station(vif, data, len);
break;
case WIFI_EVENT_MGMT_FRAME:
/* for old Marlin2 CP code or BA*/
sprdwl_event_frame(vif, data, len, 0);
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
case WIFI_EVENT_GSCAN_FRAME:
sprdwl_event_gscan_frame(vif, data, len);
break;
case WIFI_EVENT_RSSI_MONITOR:
sprdwl_event_rssi_monitor(vif, data, len);
break;
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
case WIFI_EVENT_SCAN_DONE:
sprdwl_event_scan_done(vif, data, len);
break;
case WIFI_EVENT_SDIO_SEQ_NUM:
break;
case WIFI_EVENT_MIC_FAIL:
sprdwl_event_mic_failure(vif, data, len);
break;
case WIFI_EVENT_CQM:
sprdwl_event_cqm(vif, data, len);
break;
case WIFI_EVENT_MGMT_TX_STATUS:
sprdwl_event_mlme_tx_status(vif, data, len);
break;
case WIFI_EVENT_TDLS:
sprdwl_event_tdls(vif, data, len);
break;
case WIFI_EVENT_SUSPEND_RESUME:
sprdwl_event_suspend_resume(vif, data, len);
break;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) && defined(NAN_SUPPORT)
case WIFI_EVENT_NAN:
sprdwl_event_nan(vif, data, len);
break;
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) && defined(NAN_SUPPORT) */
#ifdef UWE5621_FTR
case WIFI_EVENT_STA_LUT_INDEX:
sprdwl_event_sta_lut(vif, data, len);
break;
#endif
case WIFI_EVENT_BA:
sprdwl_event_ba_mgmt(vif, data, len);
break;
#ifdef DFS_MASTER
case WIFI_EVENT_RADAR_DETECTED:
sprdwl_11h_handle_radar_detected(vif, data, len);
break;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) && defined(RTT_SUPPORT)
case WIFI_EVENT_RTT:
sprdwl_event_ftm(vif, data, len);
break;
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) && defined(RTT_SUPPORT) */
case WIFI_EVENT_HANG_RECOVERY:
sprdwl_event_hang_recovery(vif, data, len);
break;
case WIFI_EVENT_THERMAL_WARN:
sprdwl_event_thermal_warn(vif, data, len);
break;
case WIFI_EVENT_WFD_MIB_CNT:
sprdwl_wfd_mib_cnt(vif, data, len);
break;
case WIFI_EVENT_FW_PWR_DOWN:
sprdwl_event_fw_power_down(vif, data, len);
break;
case WIFI_EVENT_SDIO_FLOWCON:
break;
case WIFI_EVENT_CHAN_CHANGED:
sprdwl_event_chan_changed(vif, data, len);
break;
case WIFI_EVENT_COEX_BT_ON_OFF:
sprdwl_event_coex_bt_on_off(data, len);
break;
default:
wl_info("unsupported event: %d\n", hdr->cmd_id);
break;
}
sprdwl_put_vif(vif);
if ((long)msg & 0x3)
kfree(data);
return plen;
}
int sprdwl_set_tlv_data(struct sprdwl_priv *priv, u8 ctx_id,
struct sprdwl_tlv_data *tlv, int length)
{
struct sprdwl_msg_buf *msg;
if (priv == NULL || tlv == NULL)
return -EINVAL;
msg = sprdwl_cmd_getbuf(priv, length, ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_TLV);
if (!msg)
return -ENOMEM;
memcpy(msg->data, tlv, length);
wl_info("%s tlv type = %d\n", __func__, tlv->type);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
void sprdwl_set_tlv_elmt(u8 *addr, u16 type, u16 len, u8 *data)
{
struct sprdwl_tlv_data *p = (struct sprdwl_tlv_data *)addr;
p->type = type;
p->len = len;
memcpy(p->data, data, len);
}
int sprdwl_set_wowlan(struct sprdwl_priv *priv, int subcmd, void *pad, int pad_len)
{
struct sprdwl_msg_buf *msg;
struct wowlan_cmd {
u8 sub_cmd_id;
u8 pad_len;
char pad[0];
} *cmd;
if (priv == NULL)
return -EINVAL;
msg = sprdwl_cmd_getbuf(priv, pad_len + sizeof(struct wowlan_cmd), SPRDWL_MODE_NONE,
SPRDWL_HEAD_RSP, WIFI_CMD_SET_WOWLAN);
if (!msg)
return -ENOMEM;
cmd = (struct wowlan_cmd *)msg->data;
cmd->sub_cmd_id = subcmd;
cmd->pad_len = pad_len;
wl_info("%s subcmd = %d, len = %d\n", __func__, cmd->sub_cmd_id, cmd->pad_len);
if (pad_len)
memcpy(cmd->pad, pad, pad_len);
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, NULL, NULL);
}
int sprdwl_sync_disconnect_event(struct sprdwl_vif *vif, unsigned int timeout)
{
int ret = 0;
reinit_completion(&vif->disconnect_completed);
vif->priv->is_suspending = 1;
sprdwl_cmd_lock(&g_sprdwl_cmd);
ret = wait_for_completion_timeout(&vif->disconnect_completed, timeout);
sprdwl_cmd_unlock(&g_sprdwl_cmd);
return ret;
}
int sprdwl_set_packet_offload(struct sprdwl_priv *priv, u8 vif_ctx_id,
u32 req, u8 enable, u32 interval,
u32 len, u8 *data)
{
struct sprdwl_msg_buf *msg;
struct sprdwl_cmd_packet_offload *p;
struct sprdwl_cmd_packet_offload *packet = NULL;
u16 r_len = sizeof(*packet);
u8 r_buf[sizeof(*packet)];
msg = sprdwl_cmd_getbuf(priv, sizeof(*p) + len, vif_ctx_id,
SPRDWL_HEAD_RSP, WIFI_CMD_PACKET_OFFLOAD);
if (!msg)
return -ENOMEM;
p = (struct sprdwl_cmd_packet_offload *)msg->data;
p->enable = enable;
p->req_id = req;
if (enable) {
p->period = interval;
p->len = len;
memcpy(p->data, data, len);
}
return sprdwl_cmd_send_recv(priv, msg, CMD_WAIT_TIMEOUT, r_buf, &r_len);
}