android13/external/wifi_driver/aic8800/aic8800_fdrv/rwnx_testmode.c

231 lines
6.0 KiB
C

/**
****************************************************************************************
*
* @file rwnx_testmode.c
*
* @brief Test mode function definitions
*
* Copyright (C) RivieraWaves 2012-2019
*
****************************************************************************************
*/
#include <net/mac80211.h>
#include <net/netlink.h>
#include "rwnx_testmode.h"
#include "rwnx_msg_tx.h"
#include "rwnx_dini.h"
#include "reg_access.h"
/*
* This function handles the user application commands for register access.
*
* It retrieves command ID carried with RWNX_TM_ATTR_COMMAND and calls to the
* handlers respectively.
*
* If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
* mandatory fields(RWNX_TM_ATTR_REG_OFFSET,RWNX_TM_ATTR_REG_VALUE32)
* are missing; Otherwise 0 is replied indicating the success of the command execution.
*
* If RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_REG_READ, the register read
* value is returned with RWNX_TM_ATTR_REG_VALUE32.
*
* @hw: ieee80211_hw object that represents the device
* @tb: general message fields from the user space
*/
int rwnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
{
struct rwnx_hw *rwnx_hw = hw->priv;
u32 mem_addr, val32;
struct sk_buff *skb;
int status = 0;
/* First check if register address is there */
if (!tb[RWNX_TM_ATTR_REG_OFFSET]) {
printk("Error finding register offset\n");
return -ENOMSG;
}
mem_addr = nla_get_u32(tb[RWNX_TM_ATTR_REG_OFFSET]);
switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
case RWNX_TM_CMD_APP2DEV_REG_READ:
{
struct dbg_mem_read_cfm mem_read_cfm;
/*** Send the command to the LMAC ***/
status = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &mem_read_cfm);
if (status)
return status;
/* Allocate the answer message */
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
if (!skb) {
printk("Error allocating memory\n");
return -ENOMEM;
}
val32 = mem_read_cfm.memdata;
if (nla_put_u32(skb, RWNX_TM_ATTR_REG_VALUE32, val32))
goto nla_put_failure;
/* Send the answer to upper layer */
status = cfg80211_testmode_reply(skb);
if (status < 0)
printk("Error sending msg : %d\n", status);
}
break;
case RWNX_TM_CMD_APP2DEV_REG_WRITE:
{
if (!tb[RWNX_TM_ATTR_REG_VALUE32]) {
printk("Error finding value to write\n");
return -ENOMSG;
} else {
val32 = nla_get_u32(tb[RWNX_TM_ATTR_REG_VALUE32]);
/* Send the command to the LMAC */
status = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, val32);
if (status)
return status;
}
}
break;
default:
printk("Unknown testmode register command ID\n");
return -ENOSYS;
}
return status;
nla_put_failure:
kfree_skb(skb);
return -EMSGSIZE;
}
/*
* This function handles the user application commands for Debug filter settings.
*
* @hw: ieee80211_hw object that represents the device
* @tb: general message fields from the user space
*/
int rwnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb)
{
struct rwnx_hw *rwnx_hw = hw->priv;
u32 filter;
int status = 0;
/* First check if the filter is there */
if (!tb[RWNX_TM_ATTR_REG_FILTER]) {
printk("Error finding filter value\n");
return -ENOMSG;
}
filter = nla_get_u32(tb[RWNX_TM_ATTR_REG_FILTER]);
RWNX_DBG("testmode debug filter, setting: 0x%x\n", filter);
switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
case RWNX_TM_CMD_APP2DEV_SET_DBGMODFILTER:
{
/* Send the command to the LMAC */
status = rwnx_send_dbg_set_mod_filter_req(rwnx_hw, filter);
if (status)
return status;
}
break;
case RWNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER:
{
/* Send the command to the LMAC */
status = rwnx_send_dbg_set_sev_filter_req(rwnx_hw, filter);
if (status)
return status;
}
break;
default:
printk("Unknown testmode register command ID\n");
return -ENOSYS;
}
return status;
}
/*
* This function handles the user application commands for register access without using
* the normal LMAC messaging way.
* This time register access will be done through direct PCI BAR windows. This can be used
* to access registers even when the :AMC FW is stuck.
*
* @hw: ieee80211_hw object that represents the device
* @tb: general message fields from the user space
*/
int rwnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb)
{
struct rwnx_hw *rwnx_hw = hw->priv;
struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
u32 mem_addr;
struct sk_buff *skb;
int status = 0;
volatile unsigned int reg_value = 0;
unsigned int offset;
/* First check if register address is there */
if (!tb[RWNX_TM_ATTR_REG_OFFSET]) {
printk("Error finding register offset\n");
return -ENOMSG;
}
mem_addr = nla_get_u32(tb[RWNX_TM_ATTR_REG_OFFSET]);
offset = mem_addr & 0x00FFFFFF;
switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
case RWNX_TM_CMD_APP2DEV_REG_READ_DBG:
{
/*** Send the command to the LMAC ***/
reg_value = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, offset);
/* Allocate the answer message */
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
if (!skb) {
printk("Error allocating memory\n");
return -ENOMEM;
}
if (nla_put_u32(skb, RWNX_TM_ATTR_REG_VALUE32, reg_value))
goto nla_put_failure;
/* Send the answer to upper layer */
status = cfg80211_testmode_reply(skb);
if (status < 0)
printk("Error sending msg : %d\n", status);
}
break;
case RWNX_TM_CMD_APP2DEV_REG_WRITE_DBG:
{
if (!tb[RWNX_TM_ATTR_REG_VALUE32]) {
printk("Error finding value to write\n");
return -ENOMSG;
} else {
reg_value = nla_get_u32(tb[RWNX_TM_ATTR_REG_VALUE32]);
/* Send the command to the LMAC */
RWNX_REG_WRITE(reg_value, rwnx_plat, RWNX_ADDR_SYSTEM,
offset);
}
}
break;
default:
printk("Unknown testmode register command ID\n");
return -ENOSYS;
}
return status;
nla_put_failure:
kfree_skb(skb);
return -EMSGSIZE;
}