/** **************************************************************************************** * * @file rwnx_testmode.c * * @brief Test mode function definitions * * Copyright (C) RivieraWaves 2012-2019 * **************************************************************************************** */ #include #include #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; }