blob: dd300d8c03e8d9a2208f948d762b92bcc765ef38 [file] [log] [blame]
/**
Copyright (c) 2008 - 2013 Quantenna Communications Inc
All Rights Reserved
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <qtn/qdrv_sch.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/stddef.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/in.h>
#include <net/sock.h>
#include <net/sch_generic.h>
#include <trace/ippkt.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include "qdrv_features.h"
#include "qdrv_debug.h"
#include "qdrv_config.h"
#include "qdrv_control.h"
#include "qdrv_pktlogger.h"
#include "qdrv_mac.h"
#include "qdrv_soc.h"
#include "qdrv_muc_stats.h"
#include "qdrv_wlan.h"
#include <linux/etherdevice.h>
#include "qdrv_radar.h"
#include <qtn/qtn_math.h>
#include "qdrv_vap.h"
#include "qdrv_bridge.h"
#include "qtn/qdrv_bld.h"
#include "qdrv_netdebug_checksum.h"
#include "qdrv_vlan.h"
#include "qdrv_mac_reserve.h"
#include <radar/radar.h>
#include <asm/board/soc.h>
#include <qtn/mproc_sync_base.h>
#include <common/ruby_version.h>
#include <common/ruby_mem.h>
#include <qtn/qtn_bb_mutex.h>
#include <qtn/hardware_revision.h>
#include <qtn/emac_debug.h>
#include <qtn/ruby_cpumon.h>
#include <qtn/qtn_muc_stats_print.h>
#include <qtn/qtn_vlan.h>
#include <asm/board/troubleshoot.h>
#include <linux/net/bridge/br_public.h>
#include "qdrv_show.h"
#include <qtn/txbf_mbox.h>
#include <net/iw_handler.h> /* wireless_send_event(..) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
#include <linux/pm_qos.h>
#include <linux/gpio.h>
#else
#include <linux/pm_qos_params.h>
#include <asm/gpio.h>
#endif
#include <qtn/topaz_tqe_cpuif.h>
#include <qtn/topaz_vlan_cpuif.h>
#include <qtn/topaz_fwt_sw.h>
#include <ruby/pm.h>
#include <ruby/gpio.h>
#ifdef MTEST
#include "../mtest/mtest.h"
#endif
static const struct qtn_auc_stat_field auc_field_stats_default[] = {
#if !defined(CONFIG_TOPAZ_PCIE_HOST)
#include <qtn/qtn_auc_stats_fields.default.h>
#endif
};
static const struct qtn_auc_stat_field auc_field_stats_nomu[] = {
#if !defined(CONFIG_TOPAZ_PCIE_HOST)
#include <qtn/qtn_auc_stats_fields.nomu.h>
#endif
};
#define STR2L(_str) (simple_strtol(_str, 0, 0))
static int qdrv_command_start(struct device *dev, int argc, char *argv[]);
static int qdrv_command_stop(struct device *dev, int argc, char *argv[]);
static int qdrv_command_get(struct device *dev, int argc, char *argv[]);
static int qdrv_command_set(struct device *dev, int argc, char *argv[]);
static int qdrv_command_read(struct device *dev, int argc, char *argv[]);
static int qdrv_command_write(struct device *dev, int argc, char *argv[]);
static int qdrv_command_calcmd(struct device *dev, int argc, char *argv[]);
static int qdrv_command_led(struct device *dev, int argc, char *argv[]);
static int qdrv_command_gpio(struct device *dev, int argc, char *argv[]);
static int qdrv_command_pwm(struct device *dev, int argc, char *argv[]);
static int qdrv_command_memdebug(struct device *dev, int argc, char *argv[]);
static int qdrv_command_radar(struct device *dev, int argc, char *argv[]);
static int qdrv_command_rifs(struct device *dev, int argc, char *argv[]);
static int qdrv_command_bridge(struct device *dev, int argc, char *argv[]);
static int qdrv_command_clearsram(struct device *dev, int argc, char *argv[]);
static int qdrv_command_dump(struct device *dev, int argc, char *argv[]);
static int qdrv_command_muc_memdbg(struct device *dev, int argc, char *argv[]);
static int qdrv_command_pktlogger(struct device *dev, int argc, char *argv[]);
static int qdrv_command_rf_reg_dump(struct device *dev, int argc, char *argv[]);
#if defined(QTN_DEBUG)
static int qdrv_command_dbg(struct device *dev, int argc, char *argv[]);
#endif
#ifdef QDRV_TX_DEBUG
static int qdrv_command_txdbg(struct device *dev, int argc, char *argv[]);
#endif
static int qdrv_command_mu(struct device *dev, int argc, char *argv[]);
unsigned int g_dbg_dump_pkt_len = 0;
unsigned int g_qos_q_merge = 0x00000000;
unsigned int g_catch_fcs_corruption = 0;
static unsigned int g_qdrv_radar_test_mode = 0;
int qdrv_radar_is_test_mode(void)
{
return !!g_qdrv_radar_test_mode;
}
int qdrv_radar_test_mode_csa_en(void)
{
return (g_qdrv_radar_test_mode == 0x3);
}
#if defined (ERICSSON_CONFIG)
int qdrv_wbsp_ctrl = 1;
#else
int qdrv_wbsp_ctrl = 0;
#endif
static u8 wifi_macaddr[IEEE80211_ADDR_LEN];
static unsigned int s_txif_list_max = QNET_TXLIST_ENTRIES_DEFAULT;
uint32_t g_carrier_id = 0;
EXPORT_SYMBOL(g_carrier_id);
#define LED_FILE "/mnt/jffs2/led.txt"
//#define LED_FILE "/scripts/led.txt"
#define QDRV_UC_STATS_DESC_LEN 35
/*
* In core.c, linux/arch/arm/mach-ums
*/
extern void set_led_gpio(unsigned int gpio_num, int val);
extern int get_led_gpio(unsigned int gpio_num);
#define ENVY2_REV_A 0x0020
static struct semaphore s_output_sem;
static struct qdrv_cb *s_output_qcb = NULL;
/* Static buffer for holding kernel crash across reboot */
static char *qdrv_crash_log = NULL;
static uint32_t qdrv_crash_log_len = 0;
static struct command_cb
{
char *command;
int (*fn)(struct device *dev, int argc, char *argv[]);
}
s_command_table[] =
{
{ "start", qdrv_command_start },
{ "stop", qdrv_command_stop },
{ "set", qdrv_command_set },
{ "get", qdrv_command_get },
{ "read", qdrv_command_read },
{ "write", qdrv_command_write },
{ "calcmd", qdrv_command_calcmd },
{ "ledcmd", qdrv_command_led },
{ "gpio", qdrv_command_gpio },
{ "pwm", qdrv_command_pwm },
{ "radar", qdrv_command_radar },
{ "bridge", qdrv_command_bridge },
{ "memdebug", qdrv_command_memdebug },
{ "clearsram", qdrv_command_clearsram },
{ "dump", qdrv_command_dump },
{ "muc_memdbg", qdrv_command_muc_memdbg },
{ "rifs", qdrv_command_rifs },
#if defined(QTN_DEBUG)
{ "dbg", qdrv_command_dbg },
#endif
{ "pktlogger", qdrv_command_pktlogger },
#ifdef QDRV_TX_DEBUG
{ "txdbg", qdrv_command_txdbg },
#endif
{ "mu", qdrv_command_mu },
{ "rf_regdump", qdrv_command_rf_reg_dump },
};
#define COMMAND_TABLE_SIZE (sizeof(s_command_table)/sizeof(struct command_cb))
#define membersizeof(type, field) \
sizeof(((type *) NULL)->field)
static struct param_cb
{
char *name;
unsigned int flags;
#define P_FL_TYPE_INT 0x00000001
#define P_FL_TYPE_STRING 0x00000002
#define P_FL_TYPE_MAC 0x00000004
char *address;
int offset;
int size;
}
s_param_table[] =
{
{
"mac0addr", P_FL_TYPE_MAC, NULL,
offsetof(struct qdrv_cb, mac0), membersizeof(struct qdrv_cb, mac0)
},
{
"mac1addr", P_FL_TYPE_MAC, NULL,
offsetof(struct qdrv_cb, mac1), membersizeof(struct qdrv_cb, mac1)
},
{
"wifimacaddr", P_FL_TYPE_MAC, (char *)&wifi_macaddr[ 0 ],
0, sizeof(wifi_macaddr)
},
{
"mucfw", P_FL_TYPE_STRING, NULL,
offsetof(struct qdrv_cb, muc_firmware), membersizeof(struct qdrv_cb, muc_firmware)
},
{
"dspfw", P_FL_TYPE_STRING, NULL,
offsetof(struct qdrv_cb, dsp_firmware), membersizeof(struct qdrv_cb, dsp_firmware)
},
{
"aucfw", P_FL_TYPE_STRING, NULL,
offsetof(struct qdrv_cb, auc_firmware), membersizeof(struct qdrv_cb, auc_firmware)
},
{
"dump_pkt_len", P_FL_TYPE_INT, (char *) &g_dbg_dump_pkt_len,
0, sizeof(g_dbg_dump_pkt_len)
},
{
"uc_flags", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"txif_list_max", P_FL_TYPE_INT, (char *) &s_txif_list_max,
0, sizeof(s_txif_list_max)
},
{
"catch_fcs_corruption", P_FL_TYPE_INT, (char *) &g_catch_fcs_corruption,
0, sizeof(g_catch_fcs_corruption)
},
{
"muc_qos_q_merge", P_FL_TYPE_INT, (char *) &g_qos_q_merge,
0, sizeof(g_qos_q_merge)
},
{
"test1", P_FL_TYPE_INT, (char *) &g_qdrv_radar_test_mode,
0, sizeof(g_qdrv_radar_test_mode)
},
{
"vendor_fix", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"vap_default_state", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"brcm_rxglitch_thrshlds", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"vlan_promisc", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"pwr_mgmt", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"rxgain_params", P_FL_TYPE_INT, NULL,
0, sizeof(u_int32_t)
},
{
"wbsp_ctrl", P_FL_TYPE_INT, (char *) &qdrv_wbsp_ctrl,
0, sizeof(qdrv_wbsp_ctrl)
},
{
"fw_no_mu", P_FL_TYPE_INT, NULL,
offsetof(struct qdrv_cb, fw_no_mu), membersizeof(struct qdrv_cb, fw_no_mu)
},
};
#define PARAM_TABLE_SIZE (sizeof(s_param_table)/sizeof(struct param_cb))
static struct qdrv_event qdrv_event_log_table[QDRV_EVENT_LOG_SIZE];
static int qdrv_event_ptr = 0;
static spinlock_t qdrv_event_lock;
struct qdrv_show_assoc_params g_show_assoc_params;
#define VERIWAVE_TXPOWER_CMD_SIZE 6
void qdrv_event_log(char *str, int arg1, int arg2, int arg3, int arg4, int arg5)
{
spin_lock(&qdrv_event_lock);
qdrv_event_log_table[qdrv_event_ptr].jiffies = jiffies;
qdrv_event_log_table[qdrv_event_ptr].clk = 0;
qdrv_event_log_table[qdrv_event_ptr].str = str;
qdrv_event_log_table[qdrv_event_ptr].arg1 = arg1;
qdrv_event_log_table[qdrv_event_ptr].arg2 = arg2;
qdrv_event_log_table[qdrv_event_ptr].arg3 = arg3;
qdrv_event_log_table[qdrv_event_ptr].arg4 = arg4;
qdrv_event_log_table[qdrv_event_ptr].arg5 = arg5;
qdrv_event_ptr = (qdrv_event_ptr + 1) & QDRV_EVENT_LOG_MASK;
spin_unlock(&qdrv_event_lock);
}
EXPORT_SYMBOL(qdrv_event_log);
/* used for send formatted string custom event IWEVCUSTOM */
int qdrv_eventf(struct net_device *dev, const char *fmt, ...)
{
va_list args;
int i;
union iwreq_data wreq;
char buffer[IW_CUSTOM_MAX];
if (dev == NULL) {
return 0;
}
/* Format the custom wireless event */
memset(&wreq, 0, sizeof(wreq));
va_start(args, fmt);
i = vsnprintf(buffer, IW_CUSTOM_MAX, fmt, args);
va_end(args);
wreq.data.length = strnlen(buffer, IW_CUSTOM_MAX);
wireless_send_event(dev, IWEVCUSTOM, &wreq, buffer);
return i;
}
EXPORT_SYMBOL(qdrv_eventf);
static int qdrv_command_is_valid_addr(unsigned long addr)
{
if (is_valid_mem_addr(addr)) {
return 1;
} else if (addr >= RUBY_ARC_CACHE_BYPASS) {
/* ARC's no-cache, TLB-bypass region where we have all our registers */
return 1;
}
return 0;
}
int qdrv_parse_mac(const char *mac_str, uint8_t *mac)
{
unsigned int tmparray[IEEE80211_ADDR_LEN];
if (mac_str == NULL)
return -1;
if (sscanf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x",
&tmparray[0],
&tmparray[1],
&tmparray[2],
&tmparray[3],
&tmparray[4],
&tmparray[5]) != IEEE80211_ADDR_LEN) {
return -1;
}
mac[0] = tmparray[0];
mac[1] = tmparray[1];
mac[2] = tmparray[2];
mac[3] = tmparray[3];
mac[4] = tmparray[4];
mac[5] = tmparray[5];
return 0;
}
static unsigned long qdrv_read_mem(unsigned long read_addr)
{
unsigned long retval = 0;
if (!qdrv_command_is_valid_addr(read_addr)) {
DBGPRINTF_E("Q driver read mem, address 0x%lx is not valid\n", read_addr);
retval = -1;
} else {
unsigned long *segvaddr = ioremap_nocache(read_addr, sizeof(*segvaddr));
if (segvaddr == NULL) {
DBGPRINTF_E("Q driver read mem, failed to remap address 0x%lx\n", read_addr);
retval = -1;
} else {
retval = *segvaddr;
iounmap(segvaddr);
}
}
return retval;
}
static void qdrv_show_memory(struct seq_file *s, void *data, size_t num)
{
struct qdrv_cb *qcb = data;
if (!qdrv_command_is_valid_addr(qcb->read_addr)) {
seq_printf(s, "%08x: invalid addr\n", qcb->read_addr);
} else {
unsigned long *segvaddr_base = ioremap_nocache(qcb->read_addr,
sizeof(*segvaddr_base) * qcb->values_per_line);
if (!segvaddr_base) {
seq_printf(s, "%08x: remapping failed\n", qcb->read_addr);
} else {
int limit = qcb->values_per_line - 1;
unsigned long *segvaddr_moving = segvaddr_base;
seq_printf(s, "%08x: %08lx", qcb->read_addr, *segvaddr_moving);
qcb->read_addr += sizeof(*segvaddr_moving);
qcb->read_count--;
segvaddr_moving++;
if (limit > qcb->read_count) {
limit = qcb->read_count;
}
if (qcb->values_per_line > 1 && limit > 0) {
int i;
for (i = 0; i < limit; i++) {
seq_printf(s, " %08lx", *segvaddr_moving);
qcb->read_addr += sizeof(*segvaddr_moving);
qcb->read_count--;
segvaddr_moving++;
}
}
seq_printf(s, "\n");
iounmap(segvaddr_base);
}
}
}
/* Post command_set processing - propagate the changes in qcb into other structures */
static void
qdrv_command_set_post(struct qdrv_cb *qcb)
{
qcb->params.txif_list_max = s_txif_list_max;
}
static struct qdrv_wlan *qdrv_control_wlan_get(struct qdrv_mac *mac)
{
if (mac->data == NULL) {
/* This will happen for PCIe host */
DBGPRINTF_N("WLAN not found\n");
}
return (struct qdrv_wlan *)mac->data;
}
static struct qdrv_mac *qdrv_control_mac_get(struct device *dev, const char *argv)
{
struct qdrv_cb *qcb = dev_get_drvdata(dev);
unsigned int unit;
if ((sscanf(argv, "%d", &unit) != 1) || (unit > (MAC_UNITS - 1))) {
DBGPRINTF_E("Invalid MAC unit %u in control command\n", unit);
return NULL;
}
if (&qcb->macs[unit].data == NULL) {
return NULL;
}
return &qcb->macs[unit];
}
struct ieee80211com *qdrv_get_ieee80211com(struct device *dev)
{
struct qdrv_wlan *qw;
struct qdrv_mac *mac = qdrv_control_mac_get(dev, "0");
if (!mac) {
return NULL;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return NULL;
}
return &qw->ic;
}
/*
* check that MuC has completed its own initialisation code, and the boot complete
* hostlink message has been processed.
*
* 1 on success, 0 otherwise
*/
static int check_muc_boot(struct device *dev, void *token)
{
(void)token;
struct qdrv_cb *qcb = dev_get_drvdata(dev);
uint32_t *resources = &qcb->resources;
uint32_t mask = QDRV_RESOURCE_WLAN | QDRV_RESOURCE_MUC_BOOTED;
char *desc;
if ((*resources & mask) != mask)
return 0;
desc = qdrv_soc_get_hw_desc(0);
if (desc[0] == '\0')
panic("QDRV: invalid bond option");
printk("QDRV: hardware is %s\n", desc);
return 1;
}
/*
* Check that vap has been created successfully, 1 on success
*/
static int check_vap_created(struct device *dev, void *token)
{
struct qdrv_cb *qcb = dev_get_drvdata(dev);
int vap = (int) token;
if (vap < 0 || vap >= QDRV_MAX_VAPS) {
DBGPRINTF_E("invalid VAP %d\n", vap);
return 1;
}
return qcb->resources & QDRV_RESOURCE_VAP(vap);
}
static int check_vap_deleted(struct device *dev, void *token)
{
struct qdrv_cb *qcb = dev_get_drvdata(dev);
int vap = (int) token;
if (vap < 0 || vap >= QDRV_MAX_VAPS) {
DBGPRINTF_E("invalid VAP %d\n", vap);
return 1;
}
return (qcb->resources & QDRV_RESOURCE_VAP(vap)) == 0;
}
/**
* block until a condition is met, which is provided by check_func.
* check_func returns 1 on successful condition.
*
* if booting the muc for the first time,
* wlan (mac->data) will not be available yet.
* assume qcb->macs[0] is the mac of interest here.
*
* returns 0 on successful condition, -1 on timeout, crash, or
* mac/wlan/shared_params not properly initialized
*/
static int qdrv_command_start_block(struct device *dev, const char* description,
int (*check_func)(struct device *cf_dev, void *cf_token), void *token)
{
const unsigned long warn_threshold_msecs = 5000;
unsigned long start_jiff = jiffies;
unsigned long deadline = start_jiff + (MUC_BOOT_WAIT_SECS * HZ);
int can_block = !in_atomic();
int ret = -1;
int complete = 0;
struct qdrv_cb *qcb = dev_get_drvdata(dev);
struct qdrv_mac *mac = &qcb->macs[0];
BUG_ON(!can_block);
BUG_ON(!mac);
#ifdef MTEST
complete = 1;
ret = 0;
#endif
while (!complete) {
if (time_after(jiffies, deadline)) {
DBGPRINTF_E("Timeout waiting for %s; waited %u seconds\n",
description, MUC_BOOT_WAIT_SECS);
complete = 1;
} else if (mac && mac->dead) {
DBGPRINTF_E("Failure waiting for %s\n",
description);
complete = 1;
} else if (check_func(dev, token)) {
unsigned long elapsed_msecs;
const char *slow_warn = "";
/* once alive, qw should be initialised */
BUG_ON(qdrv_control_wlan_get(mac) == NULL);
elapsed_msecs = jiffies_to_msecs(jiffies - start_jiff);
if (elapsed_msecs > warn_threshold_msecs) {
slow_warn = " (SLOW)";
}
printk(KERN_INFO "%s succeeded %lu.%03lu seconds%s\n",
description,
elapsed_msecs / MSEC_PER_SEC,
elapsed_msecs % MSEC_PER_SEC,
slow_warn);
complete = 1;
ret = 0;
}
if (can_block) {
msleep(1);
}
}
return ret;
}
static int qdrv_soc_get_next_devid(const struct qdrv_cb *qcb)
{
int maci;
int vdevi;
int ndevs = QDRV_RESERVED_DEVIDS;
for (maci = 0; maci < MAC_UNITS; maci++) {
for (vdevi = 0; vdevi < QDRV_MAX_VAPS; vdevi++) {
if (qcb->macs[maci].vnet[vdevi] == NULL)
break;
ndevs++;
}
}
return ndevs;
}
static int qdrv_control_mimo_mode_set(struct qdrv_mac *mac, struct net_device *vdev, int opmode)
{
struct qdrv_vap *qv = netdev_priv(vdev);
int mimo_mode;
if (qv == NULL)
return -1;
if (opmode == IEEE80211_M_HOSTAP && mac->mac_active_bss > 1)
return 0;
if (ieee80211_swfeat_is_supported(SWFEAT_ID_4X4, 0))
mimo_mode = 4;
else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0))
mimo_mode = 3;
else
mimo_mode = 2;
ieee80211_param_to_qdrv(&qv->iv, IEEE80211_PARAM_MIMOMODE, mimo_mode, NULL, 0);
return 0;
}
static int qdrv_command_dev_init(struct device *dev, struct qdrv_cb *qcb)
{
char *argv[] = { "test", "31", "0", "4", "0" };
int bond;
if (qcb->resources != 0) {
DBGPRINTF_W("Driver is already started\n");
return -1;
}
if (qdrv_soc_init(dev) < 0)
panic("Restarting due to SoC failure to initialise");
if (qdrv_command_start_block(dev, "MuC boot", &check_muc_boot, NULL))
panic("Restarting due to failed MuC");
if (get_hardware_revision() >= HARDWARE_REVISION_TOPAZ_A2) {
#ifdef CONFIG_TOPAZ_PCIE_TARGET
/* There is no bond EMAC for PCIe EP board */
bond = 0;
#else
bond = topaz_emac_get_bonding();
#endif
topaz_tqe_emac_reflect_to(TOPAZ_TQE_LHOST_PORT, bond);
}
qdrv_command_calcmd(dev, ARRAY_SIZE(argv), argv);
return 0;
}
static int qdrv_command_start(struct device *dev, int argc, char *argv[])
{
struct qdrv_cb *qcb;
struct qdrv_mac *mac;
struct net_device *vdev = NULL;
uint8_t mac_addr[IEEE80211_ADDR_LEN] = {0};
int opmode = -1;
int flags = 0;
char *p;
int dev_id;
int rc;
qcb = dev_get_drvdata(dev);
if (argc == 1) {
if (qdrv_command_dev_init(dev, qcb) < 0)
return -1;
} else if ((argc == 4) || (argc == 5)) {
for (p = argv[1]; *p != '\0'; p++) {
if (!isdigit(*p)) {
goto error;
}
}
mac = qdrv_control_mac_get(dev, argv[1]);
if (mac == NULL) {
goto error;
}
if (strncmp(argv[2], "ap", 2) == 0) {
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL, "AP\n");
opmode = IEEE80211_M_HOSTAP;
} else if (strncmp(argv[2], "sta", 3) == 0) {
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL, "STA\n");
opmode = IEEE80211_M_STA;
} else if (strncmp(argv[2], "wds", 3) == 0) {
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL, "WDS\n");
opmode = IEEE80211_M_WDS;
} else {
goto error;
}
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL,
"Interface name \"%s\"\n", argv[3]);
vdev = dev_get_by_name(&init_net, argv[3]);
if (vdev != NULL) {
DBGPRINTF_E("The device name \"%s\" already exists\n", argv[3]);
dev_put(vdev);
goto error;
}
if (argc == 5) {
if (qdrv_parse_mac(argv[4], mac_addr) < 0) {
DBGPRINTF_E("Error mac address for new vap\n");
goto error;
}
}
if (opmode == IEEE80211_M_HOSTAP &&
mac->mac_active_bss == QDRV_MAX_BSS_VAPS) {
DBGPRINTF_E("Maximum MBSSID VAPs reached (%d)\n",
QDRV_MAX_BSS_VAPS);
goto error;
}
if (opmode == IEEE80211_M_WDS &&
mac->mac_active_wds == QDRV_MAX_WDS_VAPS) {
DBGPRINTF_E("Maximum WDS peers reached (%d)\n",
QDRV_MAX_WDS_VAPS);
goto error;
}
} else if (argc == 2) {
if (strncmp(argv[1], "dsp", sizeof("dsp")) == 0)
(void) qdrv_start_dsp_only(dev);
else
goto error;
} else {
goto error;
}
/* Act on the opmode if set */
if (opmode > 0) {
dev_id = qdrv_soc_get_next_devid(qcb);
if (qdrv_soc_start_vap(qcb, dev_id, mac, argv[3], mac_addr, opmode, flags) < 0) {
DBGPRINTF_E("Failed to start VAP \"%s\"\n", argv[3]);
return -1;
}
if (qdrv_command_start_block(dev, "VAP create", &check_vap_created,
(void *)QDRV_WLANID_FROM_DEVID(dev_id) )) {
return -1;
}
if (opmode == IEEE80211_M_HOSTAP) {
mac->mac_active_bss++;
} else if (opmode == IEEE80211_M_WDS) {
mac->mac_active_wds++;
}
vdev = dev_get_by_name(&init_net, argv[3]);
if (vdev != NULL) {
rc = qdrv_control_mimo_mode_set(mac, vdev, opmode);
dev_put(vdev);
if (rc != 0) {
return -1;
}
}
}
return 0;
error:
DBGPRINTF_E("Invalid arguments to start command\n");
return -1;
}
static int qdrv_command_stop_one_vap(struct device *dev, struct qdrv_mac *mac, const char *vapname)
{
struct qdrv_cb *qcb = dev_get_drvdata(dev);
struct net_device *vdev = dev_get_by_name(&init_net, vapname);
struct qdrv_vap *qv;
enum ieee80211_opmode opmode;
unsigned int wlanid;
if (vdev == NULL) {
DBGPRINTF_E("net device \"%s\" doesn't exist\n", vapname);
return -ENODEV;
}
dev_put(vdev);
qv = netdev_priv(vdev);
opmode = qv->iv.iv_opmode;
wlanid = QDRV_WLANID_FROM_DEVID(qv->devid);
if (qdrv_vap_exit(mac, vdev) < 0) {
DBGPRINTF_E("Failed to exit VAP \"%s\"\n", vapname);
return -1;
}
if (qdrv_soc_stop_vap(qcb, mac, vdev) < 0) {
DBGPRINTF_E("Failed to stop VAP \"%s\"\n", vapname);
return -1;
}
if (opmode == IEEE80211_M_HOSTAP) {
mac->mac_active_bss--;
} else if (opmode == IEEE80211_M_WDS) {
mac->mac_active_wds--;
}
if (qdrv_command_start_block(dev, "VAP delete", &check_vap_deleted, (void *)wlanid)) {
return -1;
}
return 0;
}
static int qdrv_command_stop(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac;
char *p;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (argc == 1) {
if(qdrv_soc_exit(dev) < 0) {
return(-1);
}
} else if (argc == 3) {
const char *macstr = argv[1];
const char *vapname = argv[2];
/* Second argument must be all digits */
for (p = argv[1]; *p != '\0'; p++) {
if (!isdigit(*p)) {
goto error;
}
}
mac = qdrv_control_mac_get(dev, macstr);
if (mac == NULL) {
DBGPRINTF_E("no mac for arg: \"%s\"\n", macstr);
return -ENODEV;
}
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL,
"Interface name \"%s\"\n", vapname);
if (strncmp(vapname, "all", 3) == 0) {
int i;
int res;
struct qdrv_wlan *qw = qdrv_mac_get_wlan(mac);
for (i = QDRV_MAX_VAPS - 1; i >= 0; --i) {
if (mac->vnet[i]) {
res = qdrv_command_stop_one_vap(dev, mac, mac->vnet[i]->name);
if (res != 0)
return res;
}
}
/* deleted DFS/Radar related timers */
qdrv_radar_unload(mac);
qdrv_wlan_cleanup_before_reload(&qw->ic);
} else {
return qdrv_command_stop_one_vap(dev, mac, vapname);
}
} else {
goto error;
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
error:
DBGPRINTF_E("Invalid arguments to stop command\n");
return -1;
}
int _atoi(const char *str)
{
int rVal = 0;
int sign = 1;
if (!str)
return 0;
while (*str && (*str == ' '|| *str == '\t'))
str++;
if (*str == '\0')
return 0;
if (*str == '+' || *str == '-')
sign = (*str++ == '+') ? 1 : -1;
while (*str && *str >= '0' && *str <= '9')
rVal = rVal * 10 + *str++ - '0';
return (rVal * sign);
}
#if 0
#define DEFAULT_LENGTH 4
int qdrv_command_calcmd_from_shell (char* calcmd, char **arg, int arc)
{
int i;
char shellInterpret[128];
int length = DEFAULT_LENGTH;
for(i = 0; i < 128; i++)
{
shellInterpret[i] = 0;
}
if(strcmp("VCO", arg[1])==0)
{
shellInterpret[0] = 0x1;
shellInterpret[1] = 0x0;
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL | QDRV_LF_CALCMD, "VCO\n");
}
else if(strcmp("IQ_COMP", arg[1])==0)
{
shellInterpret[0] = 0x2;
shellInterpret[1] = 0x0;
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL | QDRV_LF_CALCMD, "IQ_COMP\n");
}
else if(strcmp("DC_OFFSET", arg[1])==0)
{
shellInterpret[0] = 0x3;
shellInterpret[1] = 0x0;
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL | QDRV_LF_CALCMD, "DC_OFFSET\n");
}
else
{
shellInterpret[0] = 0x0;
shellInterpret[1] = 0x0;
}
for(i = 2; i < arc; i++)
{
shellInterpret[i+2] = _atoi(arg[i]);
length ++;
}
shellInterpret[2] = (length & 0x00FF);
shellInterpret[3] = (length & 0xFF00)>>8;
memcpy(calcmd, shellInterpret, length);
return length;
}
#endif
#define QDRV_LED_MAX_READ_SIZE 32
int set_led_data (char *file, int data1, int data2){
int fd;
int ret_val;
int fr = -EINVAL;
int data;
int i;
char buf[QDRV_LED_MAX_READ_SIZE + 1];
mm_segment_t OldFs;
struct file *pfile;
loff_t pos = 0;
char p[QDRV_LED_MAX_READ_SIZE];
memset(buf, 0, sizeof(buf));
OldFs = get_fs();
set_fs(KERNEL_DS);
data = data1;
fd = sys_open(file, O_RDWR, 0);
if (fd < 0) {
fd = sys_open(file, O_CREAT | O_RDWR, 0666);
if (fd < 0) {
DBGPRINTF_E("Failed to Create File for LED GPIOs.\n");
goto setfs_ret;
}
}
if (fd >= 0) {
fr = sys_read(fd, buf, QDRV_LED_MAX_READ_SIZE);
if (fr == 0) {
for (i = 0; i < 2; i++) {
sprintf(p, "%d ", data);
pfile = fget(fd);
if (pfile) {
pos = pfile->f_pos;
ret_val = vfs_write(pfile, p, strlen(p), &pos);
fput(pfile);
pfile->f_pos = pos;
}
data = data2;
}
} else if (fr > 0) {
} else {
DBGPRINTF_E("Failed to read LED GPIO file\n");
}
sys_close(fd);
}
setfs_ret:
set_fs(OldFs);
return fr;
}
/* address range [begin, end], inclusive */
struct reg_range {
u32 begin;
u32 end;
const char *description;
};
/*
* List below is based on "golden values" registers file
* Commented memory ranges contains "holes" which are not
* allowed to be read or they're intentionally missed by
* qregcheck tool.
*/
static struct reg_range qdrv_hw_regs[] = {
{0xE0000000, 0xE0000FFF, "System Control regs"},
/*{0xE1007FFC, 0xE1038FFF, "switch regs"},*/
{0xE1020000, 0xE1020200, "HBM regs"},
/*{0xE2000000, 0xE200000F, "SPI0 regs"},*/
/*{0xE3100000, 0xE310009F, "AHB regs"},*/
{0xE5030000, 0xE50300FF, "Packet memory"},
{0xE5040000, 0xE5043FFF, "TCM"},
{0xE5044000, 0xE50440FF, "Global control"},
{0xE5050000, 0xE5051FFF, "Global control regs(1): TX frame processor regs"},
{0xE5052000, 0xE5052FFF, "Global control regs(2): RX frame processor regs"},
{0xE5050300, 0xE5053FFF, "Global control regs(3): shared frame processor regs"},
{0xE5090000, 0xE50904FF, "Global control regs(4)"},
{0xE6000000, 0xE60003FF, "BB Global regs"},
{0xE6010000, 0xE60103FF, "BB BP regs"},
{0xE6020000, 0xE602002B, "BB TXVEC regs"},
{0xE6030000, 0xE6030037, "BB RXVEC regs"},
{0xE6040000, 0xE604FFFF, "BB SPI0 regs"},
{0xE6050000, 0xE60504C4, "BB MIMO regs"},
{0xE6090000, 0xE60906FF, "BB TD (1) regs"},
{0xE6091000, 0xE60915FF, "BB TD (2) regs"},
{0xE6092000, 0xE60925FF, "BB TD (3) regs"},
{0xE60A1000, 0xE60A133F, "BB TD (RFC W mem 0) regs"},
{0xE60A2000, 0xE60A233F, "BB TD (RFC W mem 1) regs"},
{0xE60A3000, 0xE60A333F, "BB TD (RFC W mem 2) regs"},
{0xE60A4000, 0xE60A433F, "BB TD (RFC W mem 3) regs"},
{0xE60A5000, 0xE60A53FF, "BB TD (RFC TX mem) regs"},
{0xE60A6000, 0xE60A63FF, "BB TD (RX mem) regs"},
{0xE60B1000, 0xE60B10FF, "BB TD (gain SG) regs"},
{0xE60B2000, 0xE60B24FB, "BB TD (gain AG) regs"},
{0xE60B3000, 0xE60B313F, "BB TD (gain AG) regs"},
{0xE60B4000, 0xE60B4063, "BB TD (gain AG) regs"},
{0xE60F0000, 0xE60F0260, "BB 11B regs"},
{0xE6100000, 0xE6107FFF, "BB QMatrix regs"},
{0xE6200000, 0xE6200FFB, "BB FFT dump registers rx chain 1"},
{0xE6201000, 0xE6201FFB, "BB FFT dump registers rx chain 2"},
{0xE6202000, 0xE6202FFB, "BB FFT dump registers rx chain 3"},
{0xE6203000, 0xE6203FFB, "BB FFT dump registers rx chain 4"},
{0xE6400000, 0xE6400FFB, "BB Radar regs"},
{0xE8000000, 0xE8000800, "EMAC1 Control regs"},
/*{0xE9000000, 0xE9000bFF, "PCIE regs"},*/
/*{0xEA000000, 0xEA0003FF, "DMAC Conrol regs"},*/
{0xED000000, 0xED000800, "EMAC0 Control regs"},
{0xF0000000, 0xF00000FF, "UART1 Control regs"},
{0xF1000000, 0xF100003F, "GPIO regs"},
{0xF2000000, 0xF2000020, "SPI1 regs"},
{0xF4000000, 0xF40000AF, "Timer Control regs"},
{0xF5000000, 0xF50000FF, "UART2 Control regs"},
{0xF6000000, 0xF60008FF, "DDR Control regs"},
/*{0xF9000000, 0xF90000FC, "I2C regs"},*/
};
/*
* Variables for the register save/comparison logic. Two buffers for caching the values
* and an index into the hardware registers structure for the currently measured set
* of registers.
*/
#define QDRV_MAX_REG_MONITOR ARRAY_SIZE(qdrv_hw_regs)
#define QDRV_MAX_REG_PER_BUF 3
static u32 *p_hw_reg_buf[QDRV_MAX_REG_MONITOR][QDRV_MAX_REG_PER_BUF] = {{NULL},{NULL}};
static inline u32 qdrv_control_hwreg_get_range_len(struct reg_range *range)
{
return range->end - range->begin + 1;
}
static inline u32 qdrv_control_hwreg_get_range_reg_count(struct reg_range *range)
{
return qdrv_control_hwreg_get_range_len(range) / 4;
}
static void qdrv_control_hwreg_trigger(int set_num, int buf_num)
{
int num_regs;
int i;
u32 bytes;
u32 *ptr;
/* Address-> register count, with each register 32-bits */
num_regs = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[set_num]);
for (i = 0; i < QDRV_MAX_REG_PER_BUF - 1; i++) {
if (p_hw_reg_buf[set_num][i] == NULL) {
p_hw_reg_buf[set_num][i] = kmalloc(num_regs * sizeof(u32), GFP_KERNEL);
}
}
bytes = qdrv_control_hwreg_get_range_len(&qdrv_hw_regs[set_num]);
ptr = ioremap_nocache(qdrv_hw_regs[set_num].begin, bytes);
if (ptr && p_hw_reg_buf[set_num][buf_num]) {
memcpy(p_hw_reg_buf[set_num][buf_num], ptr, bytes);
iounmap(ptr);
}
}
static void qdrv_command_memdbg_usage(void)
{
printk("Usage:\n"
" muc_memdbg 0 dump\n"
" muc_memdbg 0 status\n"
" muc_memdbg 0 dumpcfg <cmd> <val>\n"
" %u - max file descriptors to print\n"
" %u - max node structures to print\n"
" %u - max bytes per hex dump\n"
" %u - print rate control tables (0 or 1)\n"
" %u - send MuC msgs to netdebug (0 or 1)\n"
" %u - print MuC trace msgs (0 or 1)\n",
QDRV_CMD_MUC_MEMDBG_FD_MAX,
QDRV_CMD_MUC_MEMDBG_NODE_MAX,
QDRV_CMD_MUC_MEMDBG_DUMP_MAX,
QDRV_CMD_MUC_MEMDBG_RATETBL,
QDRV_CMD_MUC_MEMDBG_MSG_SEND,
QDRV_CMD_MUC_MEMDBG_TRACE);
}
void qdrv_control_sysmsg_timer(unsigned long data)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) data;
struct qdrv_pktlogger_types_tbl *tbl =
qdrv_pktlogger_get_tbls_by_id(QDRV_NETDEBUG_TYPE_SYSMSG);
if (!tbl) {
return;
}
qdrv_control_sysmsg_send(qw, NULL, 0, 1);
mod_timer(&qw->pktlogger.sysmsg_timer, jiffies + (tbl->interval * HZ));
}
static void qdrv_command_muc_memdbgcfg(struct qdrv_mac *mac, struct qdrv_wlan *qw,
unsigned int param, unsigned int val)
{
struct qdrv_pktlogger_types_tbl *tbl = NULL;
if (param == QDRV_CMD_MUC_MEMDBG_MSG_SEND) {
mac->params.mucdbg_netdbg = !!val;
if (mac->params.mucdbg_netdbg == 0) {
qdrv_control_sysmsg_send(qw, NULL, 0, 1);
del_timer(&qw->pktlogger.sysmsg_timer);
} else {
tbl = qdrv_pktlogger_get_tbls_by_id(QDRV_NETDEBUG_TYPE_SYSMSG);
if (!tbl) {
return;
}
init_timer(&qw->pktlogger.sysmsg_timer);
qw->pktlogger.sysmsg_timer.function = qdrv_control_sysmsg_timer;
qw->pktlogger.sysmsg_timer.data = (unsigned long)qw;
mod_timer(&qw->pktlogger.sysmsg_timer, jiffies +
(tbl->interval * HZ));
}
}
qdrv_hostlink_msg_cmd(qw, IOCTL_DEV_CMD_MEMDBG_DUMPCFG, (param << 16) | val);
}
static int qdrv_command_muc_memdbg(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac;
struct qdrv_wlan *qw;
u_int32_t arg = 0;
unsigned int param;
unsigned int val;
if (argc < 3) {
qdrv_command_memdbg_usage();
return 0;
}
mac = qdrv_control_mac_get(dev, argv[1]);
if (mac == NULL) {
return -1;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return -1;
}
if (strcmp(argv[2], "dump") == 0) {
if (argc > 3) {
if (strcmp(argv[3], "-v") == 0) {
arg |= 0x1;
} else {
qdrv_command_memdbg_usage();
return 0;
}
}
qdrv_hostlink_msg_cmd(qw, IOCTL_DEV_CMD_MEMDBG_DUMP, arg);
} else if (strcmp(argv[2], "dumpcfg") == 0) {
if (argc < 5) {
qdrv_command_memdbg_usage();
return 0;
}
sscanf(argv[3], "%u", &param);
sscanf(argv[4], "%u", &val);
qdrv_command_muc_memdbgcfg(mac, qw, param, val);
} else if (strcmp(argv[2], "status") == 0) {
qdrv_command_muc_memdbgcfg(mac, qw, 0, 0);
} else if (strcmp(argv[2], "dumpnodes") == 0) {
qdrv_hostlink_msg_cmd(qw, IOCTL_DEV_CMD_MEMDBG_DUMPNODES, arg);
} else {
qdrv_command_memdbg_usage();
return 0;
}
return 0;
}
struct qdrv_hwreg_print_data {
struct seq_file *s;
unsigned int set_num;
unsigned int buf1_num;
unsigned int buf2_num;
};
#define QDRV_HWREG_PRINT_BUF_LEN 1024
static void qdrv_dump_hwreg_print_func(struct qdrv_hwreg_print_data *sets, const char *f, ...)
{
char buf[QDRV_HWREG_PRINT_BUF_LEN] = {0};
va_list args;
va_start(args, f);
vsnprintf(buf, QDRV_HWREG_PRINT_BUF_LEN - 1, f, args);
va_end(args);
if (sets != NULL && sets->s != NULL) {
seq_printf(sets->s, buf);
} else {
printk(buf);
}
}
#define QDRV_HWREG_MIN_ARGS_CNT 4
#define QDRV_HWREG_MAX_ARGS_CNT 6
static void qdrv_dump_hwreg_one_reg_output(struct seq_file *s, void *data, uint32_t num)
{
struct qdrv_hwreg_print_data *sets = (struct qdrv_hwreg_print_data*)data;
int len = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[sets->set_num]);
if (sets == NULL) {
return;
}
sets->s = s;
/* Inverse counter because seq file iterator uses reverse direction */
num = len - num;
if (num >= len) {
printk("%s: seq file iterator: bad register number %u\n", __func__, num);
return;
}
if (num == 0) {
qdrv_dump_hwreg_print_func(sets, "# Mac reg set %s:\n",
qdrv_hw_regs[sets->set_num].description);
}
qdrv_dump_hwreg_print_func(sets, s == NULL ? "%d: 0x%08X: 0x%08X " : "%d: 0x%08X: 0x%08X\n",
num,
qdrv_hw_regs[sets->set_num].begin + (num*4),
p_hw_reg_buf[sets->set_num][sets->buf1_num][num]);
if (s == NULL && num && ((num % 2) == 0)) {
qdrv_dump_hwreg_print_func(sets, "\n");
}
}
static void qdrv_dump_hwreg_printk_output(struct qdrv_hwreg_print_data *sets)
{
int len = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[sets->set_num]);
int i;
/*
* Index starts from 1 to allow qdrv_dump_hwreg_one_reg_output compatibility with seq files output.
*/
for (i = 1; i <= len; i++) {
qdrv_dump_hwreg_one_reg_output(NULL, (void*)sets, i);
}
}
#define QDRV_HWREGPRINT_MIN_ARGS_CNT 2
#define QDRV_HWREGPRINT_MAX_ARGS_CNT 3
static void qdrv_dump_hwregprint_one_group(struct seq_file *s, void *data, uint32_t grp_num)
{
struct qdrv_hwreg_print_data *sets = (struct qdrv_hwreg_print_data*)data;
if (sets != NULL) {
sets->s = s;
}
/* Inverse counter because seq file iterator uses reverse direction */
grp_num = QDRV_MAX_REG_MONITOR - grp_num;
if (grp_num >= QDRV_MAX_REG_MONITOR) {
printk("%s: seq file iterator: bad group number at %u\n", __func__, grp_num);
return;
}
qdrv_dump_hwreg_print_func(sets, "Set %d (%s, 0x%08X->0x%08X)\n",
grp_num, qdrv_hw_regs[grp_num].description,
qdrv_hw_regs[grp_num].begin,
qdrv_hw_regs[grp_num].end);
}
static void qdrv_dump_hwregprint_printk_output(struct qdrv_hwreg_print_data *sets)
{
int grp_num;
qdrv_dump_hwreg_print_func(sets, "\n");
for (grp_num = QDRV_MAX_REG_MONITOR; grp_num > 0; --grp_num) {
qdrv_dump_hwregprint_one_group(NULL, (void*)sets, grp_num);
}
}
#define QDRV_HWREGCMP_MIN_ARGS_CNT 3
#define QDRV_HWREGCMP_MAX_ARGS_CNT 6
static void qdrv_dump_hwregcmp_one_reg(struct seq_file *s, void *data, uint32_t reg_num)
{
struct qdrv_hwreg_print_data *sets = (struct qdrv_hwreg_print_data*)data;
int set_num = sets->set_num,
buf_1_num = sets->buf1_num,
buf_2_num = sets->buf2_num,
reg_count;
sets->s = s;
if (!p_hw_reg_buf[set_num][buf_1_num] || !p_hw_reg_buf[set_num][buf_2_num]) {
return;
}
reg_count = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[set_num]);
/* Inverse counter because seq file iterator uses reverse direction */
reg_num = reg_count - reg_num;
if (reg_num > reg_count) {
printk("%s: seq file iterator: bad register number %u\n", __func__, reg_num);
return;
}
if (reg_num == 0) {
qdrv_dump_hwreg_print_func(sets, "# Mac reg set %s:\n",
qdrv_hw_regs[sets->set_num].description);
}
if (p_hw_reg_buf[set_num][buf_1_num][reg_num] != p_hw_reg_buf[set_num][buf_2_num][reg_num]) {
qdrv_dump_hwreg_print_func(sets, "0x%08X: %08X -> %08X\n",
qdrv_hw_regs[set_num].begin + (reg_num * 4),
p_hw_reg_buf[set_num][buf_1_num][reg_num],
p_hw_reg_buf[set_num][buf_2_num][reg_num], NULL);
} else if (sets->s != NULL) {
qdrv_dump_hwreg_print_func(sets, "0x%08X: ===== %08X =====\n",
qdrv_hw_regs[set_num].begin + (reg_num * 4),
p_hw_reg_buf[set_num][buf_2_num][reg_num], NULL);
}
}
static void qdrv_dump_hwregcmp_all(struct qdrv_hwreg_print_data *sets)
{
register int reg_count, reg_num;
reg_count = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[sets->set_num]);
for (reg_num = 1; reg_num <= reg_count; reg_num++) {
qdrv_dump_hwregcmp_one_reg(NULL, (void*)sets, reg_num);
}
}
void qdrv_control_dump_active_hwreg(void)
{
int i;
for (i = 0; i < QDRV_MAX_REG_MONITOR; i++) {
if (p_hw_reg_buf[i][0]) {
/*
* If the buffer is allocated, do one final dump in the final index.
*/
qdrv_control_hwreg_trigger(i, QDRV_MAX_REG_PER_BUF - 1);
printk("\nDump active registers for set %d (%s)\n",
i, qdrv_hw_regs[i].description);
qdrv_dump_hwregcmp_all(&(struct qdrv_hwreg_print_data){NULL,
i, 0, QDRV_MAX_REG_PER_BUF - 1});
}
}
}
static void qdrv_dump_hwregcmp(int argc, char *argv[])
{
static struct qdrv_hwreg_print_data sets = {NULL, 0, 1};
int qdrvdata_output = 0;
if (argc < QDRV_HWREGCMP_MIN_ARGS_CNT || argc > QDRV_HWREGCMP_MAX_ARGS_CNT) {
printk("Bad arguments count\n");
return;
}
sscanf(argv[2], "%u", &sets.set_num);
if (sets.set_num > QDRV_MAX_REG_MONITOR - 1) {
printk("Buffer set value out of range - should be between 0 and %d\n",
QDRV_MAX_REG_MONITOR - 1);
return;
}
if (argc > 3) {
if (strcmp("--qdrvdata", argv[argc - 1]) == 0) {
qdrvdata_output = 1;
}
sscanf(argv[3], "%u", &sets.buf1_num);
sscanf(argv[4], "%u", &sets.buf2_num);
}
if (sets.buf1_num > QDRV_MAX_REG_PER_BUF - 1 || sets.buf2_num > QDRV_MAX_REG_PER_BUF - 1) {
printk("Invalid buffer index\n");
return;
}
if (qdrvdata_output == 1) {
int reg_count = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[sets.set_num]);
qdrv_control_set_show(qdrv_dump_hwregcmp_one_reg, &sets, reg_count, 1);
} else {
qdrv_dump_hwregcmp_all(&sets);
}
}
static void qdrv_dump_hwreg(int argc, char *argv[])
{
static struct qdrv_hwreg_print_data sets = {NULL, 0, 0};
int qdrvdata_output = 0;
int verbose = 0;
if (argc < QDRV_HWREG_MIN_ARGS_CNT || argc > QDRV_HWREG_MAX_ARGS_CNT) {
printk("Bad arguments count\n");
return;
}
sscanf(argv[2], "%u", &sets.set_num);
if (sets.set_num > QDRV_MAX_REG_MONITOR - 1) {
printk("Buffer value out of range - should be between 0 and %d\n",
QDRV_MAX_REG_MONITOR - 1);
return;
}
sscanf(argv[3], "%u", &sets.buf1_num);
if (sets.buf1_num > (QDRV_MAX_REG_PER_BUF - 1)) {
printk("Buffer value out of range - should be between 0 and %d\n",
QDRV_MAX_REG_PER_BUF - 1);
return;
}
printk("[%d]Dump register set \"%s\"\n", sets.set_num, qdrv_hw_regs[sets.set_num].description);
if (argc > QDRV_HWREG_MIN_ARGS_CNT) {
int i;
for (i = QDRV_HWREG_MIN_ARGS_CNT; i < argc; ++i) {
if (strcmp("--qdrvdata", argv[i]) == 0) {
qdrvdata_output = 1;
} else if (strcmp("--verbose", argv[i]) == 0) {
verbose = 1;
} else {
printk("Unknown option \"%s\"\n", argv[i]);
return;
}
}
}
if (qdrvdata_output && !verbose)
printk("Option --qdrvdata doesn't make sense without --verbose option\n");
qdrv_control_hwreg_trigger(sets.set_num, sets.buf1_num);
if (verbose) {
if (qdrvdata_output) {
int len = qdrv_control_hwreg_get_range_reg_count(&qdrv_hw_regs[sets.set_num]);
qdrv_control_set_show(qdrv_dump_hwreg_one_reg_output, &sets, len, 1);
} else {
qdrv_dump_hwreg_printk_output(&sets);
}
}
}
static void qdrv_dump_hwregprint(int argc, char *argv[])
{
static struct qdrv_hwreg_print_data sets = {NULL, 0, 0};
int qdrvdata_output = 0;
printk("\n");
if (argc > QDRV_HWREGPRINT_MAX_ARGS_CNT) {
printk("Bad arguments\n");
return;
}
if (argc > QDRV_HWREGPRINT_MIN_ARGS_CNT && strcmp(argv[2], "--qdrvdata") == 0) {
qdrvdata_output = 1;
}
if (qdrvdata_output) {
qdrv_control_set_show(qdrv_dump_hwregprint_one_group, &sets, (u32)QDRV_MAX_REG_MONITOR, 1);
} else {
qdrv_dump_hwregprint_printk_output(&sets);
}
}
static void qdrv_dump_irqstatus(void)
{
uint32_t *ptr = ioremap_nocache(0xe6000320, 16);
uint32_t val = *ptr;
printf("BB IRQ status\n");
printf("0xe6000320:%08x\n",val);
printf("td_sigma_hw_noise:%x, radar:%x, rfc:%x, dleaf_overflow:%x, leaf_overflow:%x\n",
val & 1,(val>>1)&0x3f,(val>>7)&3,(val>>9)&1,(val>>10)&1);
printf("tx_td_overflow:%x, com_mem:%x, tx_td_underflow:%x, rfic:%x\n",
(val>>11)&1,(val>>12)&0x1,(val>>13)&1,(val>>14)&1);
printf("rx_sm_watchdog:%x, tx_sm_watchdog:%x, main_sm_watchdog:%x, hready_watchdog:%x\n",
(val>>15)&1,(val>>16)&0x1,(val>>17)&1,(val>>18)&1);
val = *(u32 *)((u32)ptr + 4);
printf("0xe6000324:%08x\n",val);
printf("rx_done:%x, rx_phase:%x, rx_start:%x, tx_done:%x, tx_phase:%x, tx_start:%x \n",
(val>>0)&1,(val>>1)&0x1,(val>>2)&1,(val>>3)&1,(val>>4)&1,(val>>5)&1);
iounmap(ptr);
}
static void qdrv_dump_txstatus(void)
{
uint32_t *ptr = ioremap_nocache(0xe5050000, 0x800);
uint32_t *ptr1 = ioremap_nocache(0xe5040000, 0x200);
uint32_t read_ptr;
uint32_t temp;
uint32_t i;
/* For read and write pointers, the MSB is ignored */
for (i = 0; i < 4; i++) {
printk("Queue %x, %x wr:%03x rd:%03x ",
i, (u32)(0xe5040000 + i * 32),
*(u32 *)((u32)ptr + 0x308 + i * 16) + 0xe5030000,
*(u32 *)((u32)ptr + 0x30c + i * 16) + 0xe5030000);
temp = *(u32 *)((u32)ptr + 0x400 + i * 4);
printk("level %d, wrptr:%x, rdptr:%x en:%x ",
temp & 0xf, (temp >> 4) & 0xf, (temp >> 9) & 0x3, (temp >> 31) & 1);
read_ptr = (temp >> 9) & 0x3;
printk("frm:%08x %08x\n",
*(u32 *)((u32)ptr1 + i * 32 + read_ptr * 8),
*(u32 *)((u32)ptr1 + i * 32 + 4 + read_ptr * 8));
}
for (i = 0; i < 4; i++) {
read_ptr = ((*(u32 *)((u32)ptr + 0x410 + i * 4) & 0xf) -
((*(u32 *)((u32)ptr + 0x410 + i * 4) >> 26) & 0xf)) & 0xf;
printk("Timer0 %x, %x wr:%03x rd:%03x ",
i, (u32)(0xe5040000 + 0x80 + i * 32),
*(u32 *)((u32)ptr + 0x348 + i * 16) + 0xe5030000,
*(u32 *)((u32)ptr + 0x34c + i * 16) + 0xe5030000);
temp = *(u32 *)((u32)ptr + 0x400 + i * 4);
printk("level %d, wrptr:%x, rdptr:%x en:%x ",
temp & 0xf, (temp >> 4) & 0xf, (temp >> 9) & 0x3, (temp >> 31) & 1);
read_ptr = (temp >> 9) & 0x3;
printk("frm:%08x %08x\n",
*(u32 *)((u32)ptr1 + i * 32 + read_ptr * 8 + 0x80),
*(u32 *)((u32)ptr1 + i * 32 + 4 + read_ptr * 8 + 0x80));
}
for (i = 0; i < 4; i++) {
read_ptr =((*(u32 *)((u32)ptr + 0x420 + i * 4) & 0xf) -
((*(u32 *)((u32)ptr + 0x420 + i * 4) >> 26) & 0xf)) & 0xf;
printk("Timer1 %x, %x wr:%03x rd:%03x ",
i, (u32)(0xe5040000 + 0x100 + i * 32),
*(u32 *)((u32)ptr + 0x388 + i * 16) + 0xe5030000,
*(u32 *)((u32)ptr + 0x38c + i * 16) + 0xe5030000);
temp = *(u32 *)((u32)ptr + 0x400 + i * 4);
printk("level %d, wrptr:%x, rdptr:%x en:%x ",
temp & 0xf, (temp >> 4) & 0xf, (temp >> 9) & 0x3, (temp >> 31) & 1);
read_ptr = (temp >> 9) & 0x3;
printk("frm:%08x %08x\n",
*(u32 *)((u32)ptr1 + i * 32 + read_ptr * 8 + 0x100),
*(u32 *)((u32)ptr1 + i * 32 + 4 + read_ptr * 8 + 0x100));
}
iounmap(ptr);
iounmap(ptr1);
}
static void qdrv_dump_event_log(void)
{
char buffer[256] = {0};
int i;
for (i = 0; i < QDRV_EVENT_LOG_SIZE; i++) {
if (qdrv_event_log_table[i].str == NULL) {
break;
}
sprintf(buffer,qdrv_event_log_table[i].str,qdrv_event_log_table[i].arg1,
qdrv_event_log_table[i].arg2,
qdrv_event_log_table[i].arg3,
qdrv_event_log_table[i].arg4,
qdrv_event_log_table[i].arg5);
printf("%08x:%08x %s\n",qdrv_event_log_table[i].jiffies,qdrv_event_log_table[i].clk,buffer);
}
}
static void qdrv_dump_muc_log(struct device *dev)
{
extern int qdrv_dump_log(struct qdrv_wlan *qw);
struct qdrv_cb *qcb = dev_get_drvdata(dev);
struct qdrv_wlan *qw = (struct qdrv_wlan *)qcb->macs[0].data;
qdrv_dump_log(qw);
}
static void qdrv_dump_fctl(int argc, char *argv[])
{
uint32_t *ptr = NULL;
uint32_t *ptr1 = NULL;
uint32_t temp = 0;
char frame_type[10][12] = {"0 prestore","1 manage","2 data","3 A-MSDU","4-A-MPDU","5 beacon","6 probe","7 vht bf", "8 ht bf", "unknown"};
sscanf(argv[2],"%x",&temp);
if (temp == 0) {
printk("invalid address!\n");
return;
}
ptr = ioremap_nocache(temp, 32 * 4);
if (ptr == NULL)
return;
ptr1 = ptr;
temp = *ptr1++;
printk("\nframe_type %s",frame_type[min((temp>>4) & 0xf, (u32)9)]);
printk("\nnot sounding: %8x",(temp >> 3) & 0x1 );
printk("\trate table: %8x",(temp >> 8) & 0x1 );
printk("\tUse RA: %8x",(temp >> 9) & 0x1 );
printk("\tburst OK: %8x",(temp >> 10) & 0x1 );
printk("\nRIFS EN: %8x",(temp >> 11) & 0x1 );
printk("\tDur Upadate: %8x",(temp >> 12) & 0x1 );
printk("\tencrypt en: %8x",(temp >> 13) & 0x1 );
printk("\tignore cca: %8x",(temp >> 14) & 0x1 );
printk("\nignore nav: %8x",(temp >> 15) & 0x1 );
printk("\tmore frag: %8x",(temp >> 16) & 0x1 );
printk("\tnode en: %8x",(temp >> 17) & 0x1 );
printk("\tprefetch en: %8x",(temp >> 18) & 0x1 );
printk("\nint en: %8x",(temp >> 19) & 0x1 );
printk("\tfirst seq sel:%8x",(temp >> 21) & 0x1 );
printk("\thdr len: %8x",(temp >> 24) & 0xff );
temp = *ptr1++;
printk("\tframe Len: %8x",(temp >> 0) & 0xffff );
printk("\nprefectch Len:%8x",(temp >> 16) & 0xffff );
temp = *ptr1++;
printk("\tprefetch addr:%08x",temp);
temp = *ptr1++;
printk("\tDMA src addr: %08x",temp);
temp = *ptr1++;
printk("\tDMA next addr:%08x",temp);
temp = *ptr1++;
printk("\nnDMA Len: %8x",(temp >> 0) & 0xffff );
printk("\tRFTx Pwr0: %8x",(temp >> 16) & 0xff );
printk("\tRFTx Pwr0: %8x",(temp >> 24) & 0xff );
temp = *ptr1++;
printk("\tAIFSN: %8x ",(temp >> 0) & 0xf );
printk("\nECWmin: %8x",(temp >> 4) & 0xf );
printk("\tECWmax: %8x",(temp >> 8) & 0xf );
printk("\tNode Cache: %8x",(temp >> 12) & 0x7f );
printk("\tHW DMA: %8x",(temp >> 19) & 0x1 );
printk("\nFrm TimeoutEn:%8x",(temp >> 23) & 0x1 );
printk("\tTXOP En: %8x",(temp >> 24) & 0x1 );
printk("\tA-MPDU Den: %8x",(temp >> 25) & 0x7 );
printk("\tSM Tone Grp: %8x",(temp >> 29) & 0x7 );
temp = *ptr1++;
printk("\nFrm timeout: %8x",temp);
temp = *ptr1++;
printk("\tTx Status: %8x",temp);
temp = *ptr1++;
printk("\tRA[31:0]: %8x",temp);
temp = *ptr1++;
printk("\tRA[47:32]: %8x",(temp >> 0) & 0xffff );
printk("\nRateRty 0: %8x",0xe5040000 + ((temp >> 16) & 0xffff));
temp = *ptr1++;
printk("\tRateRty 1: %8x",0xe5040000 + ((temp >> 0) & 0xffff ));
printk("\tNxt Frm Len: %8x",(temp >> 16) & 0xffff );
temp = *ptr1++;
printk("\tEDCA TxOpLim: %8x ",(temp >> 0) & 0xff );
printk("\nTxScale: %8x",(temp >> 8) & 0x7 );
printk("\tSmoothing: %8x",(temp >> 19) & 0x1 );
printk("\tNot Sounding: %8x",(temp >> 20) & 0x1 );
printk("\tshort GI: %8x",(temp >> 24) & 0x1 );
temp = *ptr1++;
printk("\nAntSelPtr: %8x",(temp >> 0) & 0xffff );
printk("\tFxdRateSeqPtr:%8x",(temp >> 16) & 0xffff );
temp = *ptr1++;
printk("\tNumSubFrames: %8x",(temp >> 0) & 0xffff );
printk("\tTotalDenBytes:%8x",(temp >> 16) & 0xffff );
temp = *ptr1++;
printk("\nPN[31:0]: %8x",temp);
temp = *ptr1++;
printk("\tPN[47:0]: %8x",(temp >> 0) & 0xffff );
printk("\tRxTxPwr 2: %8x",(temp >> 16) & 0xff );
printk("\tRxTxPwr 3: %8x",(temp >> 24) & 0xff );
temp = *ptr1++;
printk("\nTxService: %8x",(temp >> 0) & 0xffff );
printk("\tLSIG Rsvd: %8x",(temp >> 16) & 1 );
printk("\tHTSIG Rsvd: %8x",(temp >> 17) & 1 );
printk("\n");
iounmap(ptr);
}
static void qdrv_dump_rrt(int argc, char *argv[])
{
uint32_t *ptr = NULL;
uint32_t *ptr1 = NULL;
uint32_t temp = 0;
int not_done = 4;
sscanf(argv[2],"%x",&temp);
if (temp == 0) {
printk("invalid address!\n");
return;
}
ptr = ioremap_nocache(temp, 32 * 4);
if (ptr == NULL)
return;
ptr1 = ptr;
do {
temp = *ptr1++;
printk("\nRateInd: %8x",(temp >> 0) & 0x7f );
printk("\tLongPre: %8x",(temp >> 7) & 0x1 );
printk("\t11n: %8x",(temp >> 8) & 0x1 );
printk("\tBW: %8x",(temp >> 9) & 0x3 );
printk("\nChOff: %8x",(temp >> 11) & 0x3 );
printk("\tNEss: %8x",(temp >> 13) & 0x3 );
printk("\tShortGI: %8x",(temp >> 15) & 0x1 );
printk("\tCount: %8x",(temp >> 16) & 0xf );
printk("\nAntSet: %8x",(temp >> 20) & 0xf );
printk("\tAntSetOn: %8x",(temp >> 24) & 0x1 );
printk("\tNTx: %8x",(temp >> 25) & 0x3 );
printk("\tSTBC: %8x",(temp >> 27) & 0x3 );
printk("\nExpMatType: %8x",(temp >> 29) & 0x7 );
temp = *ptr1++;
printk("\tSeqPtr: %8x",(temp >> 0) & 0x7fff);
printk("\tExpMatPtr: %8x",(temp >> 16) & 0x3f);
printk("\tLDPC: %8x",(temp >> 26) & 0x1);
printk("\nLDPCAdj: %8x",(temp >> 27) & 0x1);
printk("\tShiftVal: %8x",(temp >> 28) & 0x7);
printk("\tLastEntry: %8x",(temp >> 31) & 0x1);
printk("\t11ac: %8x",(temp >> 15) & 0x1);
not_done--;
} while (((temp & 0x80000000)==0) && not_done);
printk("\n");
iounmap(ptr);
}
static void qdrv_dump_mem(int argc, char *argv[])
{
uint32_t *ptr;
int num_bytes = 256;
uint32_t addr;
int i;
if (argc < 3) {
printk("dump mem <addr> [num bytes]\n");
return;
}
sscanf(argv[2],"%x",&addr);
// if ddr addr, add the ddr bit otherwise assume addr
// is correct (i.e. user has to correct for sram addr
addr &= 0xfffffffc;
if (addr < 0x80000000) {
addr |= 0x80000000;
}
if (!qdrv_command_is_valid_addr(addr)) {
printk("invalid address\n");
return;
}
if (argc >= 4) {
sscanf(argv[3],"%x",&num_bytes);
}
ptr = ioremap_nocache(addr, num_bytes);
if (!ptr) {
printk("remapping failed\n");
} else {
for (i = 0; i < num_bytes; i += 4) {
if ((i%16) == 0) {
printk("\n%08x: ",addr + i);
}
printk("%08x ",*(u32 *)((int)ptr + i));
}
printk("\n");
iounmap(ptr);
}
}
static void qdrv_dump_dma(int argc, char *argv[])
{
uint32_t *ptr = NULL;
int addr;
if (argc < 3) {
printk("dump dma <addr>\n");
return;
}
sscanf(argv[2], "%x", &addr);
addr &= 0xfffffffc;
while (addr != 0) {
uint8_t *data_ptr;
uint32_t src;
int i;
// convert from muc addr
if (addr < 0x80000000) {
addr |= 0x80000000;
} else {
addr |= 0x08000000;
}
ptr = ioremap_nocache(addr, 4 * 4);
if (ptr == NULL) {
printk("invalid address!\n");
return;
}
printk("\nsrc: %08x",ptr[0]);
printk(" dst: %08x",ptr[1]);
printk(" next: %08x",ptr[2]);
printk(" len: %08x",ptr[3]&0xffff);
addr = ptr[2];
src = ptr[0];
iounmap(ptr);
if (src < 0x80000000) {
src |= 0x80000000;
} else {
src |= 0x08000000;
}
data_ptr = ioremap_nocache(src, 8);
if (data_ptr == NULL) {
printk("invalid data_ptr!\n");
return;
}
for (i = 0; i < 8; i++) {
printf(" %02x", data_ptr[i]);
}
printk("\n");
iounmap(data_ptr);
}
}
static void qdrv_dump_usage(void)
{
printk("usage: dump irqstatus\n");
printk("usage: dump txstatus\n");
printk("usage: dump log\n");
printk("usage: dump muc\n");
printk("usage: dump fctl\n");
printk("usage: dump rrt\n");
printk("usage: dump mem <addr> [num bytes]\n");
printk("usage: dump dma <addr>\n");
printk("usage: dump hwregprint [--qdrvdata]\n");
printk("usage: dump hwreg <reg set: 0-%d> <buf num: 0-%d> [--verbose [--qdrvdata]]\n",
QDRV_MAX_REG_MONITOR - 1, QDRV_MAX_REG_PER_BUF - 1);
printk("usage: dump hwregcmp <set: 0-%d> [<buf 1: 0-%d> <buf 2: 0-%d>] [--qdrvdata]\n",
QDRV_MAX_REG_MONITOR - 1, QDRV_MAX_REG_PER_BUF - 1, QDRV_MAX_REG_PER_BUF - 1);
#if QTN_SEM_TRACE
printk("usage: dump sem\n");
#endif
}
static int qdrv_command_dump(struct device *dev, int argc, char *argv[])
{
if (argc < 2) {
qdrv_dump_usage();
return 0;
}
if (strcmp(argv[1], "hwregprint") == 0) {
qdrv_dump_hwregprint(argc, argv);
} else if (strcmp(argv[1], "hwreg") == 0) {
qdrv_dump_hwreg(argc, argv);
} else if (strcmp(argv[1], "hwregcmp") == 0) {
qdrv_dump_hwregcmp(argc, argv);
} else if (strcmp(argv[1], "irqstatus") == 0) {
qdrv_dump_irqstatus();
} else if (strcmp(argv[1],"txstatus") == 0) {
qdrv_dump_txstatus();
} else if (strcmp(argv[1],"log") == 0) {
qdrv_dump_event_log();
} else if (strcmp(argv[1],"muc") == 0) {
qdrv_dump_muc_log(dev);
} else if (strcmp(argv[1],"fctl") == 0) {
qdrv_dump_fctl(argc, argv);
} else if (strcmp(argv[1],"rrt") == 0) {
qdrv_dump_rrt(argc, argv);
} else if (strcmp(argv[1],"mem") == 0) {
qdrv_dump_mem(argc, argv);
} else if (strcmp(argv[1],"dma") == 0) {
qdrv_dump_dma(argc, argv);
#if QTN_SEM_TRACE
} else if (strcmp(argv[1],"sem") == 0) {
qtn_mproc_sync_spin_lock_log_dump();
#endif
} else {
printk("%s: invalid dump type %s\n", __func__, argv[1]);
}
return 0;
}
static int qdrv_command_radar(struct device *dev, int argc, char *argv[])
{
if ((3 <= argc) && (strcmp(argv[1], "enable")==0)) {
qdrv_radar_enable(argv[2]);
} else if ((2 <= argc) && (strcmp(argv[1], "disable")==0)) {
qdrv_radar_disable();
} else {
goto usage;
}
return 0;
usage:
printk("usage: %s (enable <region>|disable)\n", argv[0]);
return 0;
}
static int qdrv_command_rifs(struct device *dev, int argc, char *argv[])
{
if (argc == 2 && strcmp(argv[1], "enable") == 0) {
qtn_rifs_mode_enable(QTN_LHOST_SOC_CPU);
} else if (argc == 2 && strcmp(argv[1], "disable") == 0) {
qtn_rifs_mode_disable(QTN_LHOST_SOC_CPU);
} else {
printk("usage: %s (enable|disable)\n", argv[0]);
}
return 0;
}
static int qdrv_command_led (struct device *dev, int argc, char *argv[]){
int ret_val, data1, data2;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Check that we have all the arguments */
if(argc != 3) {
goto error;
}
if(sscanf(argv[1], "%d", &data1) != 1) {
goto error;
}
if(sscanf(argv[2], "%d", &data2) != 1) {
goto error;
}
ret_val = set_led_data(LED_FILE, data1, data2);
if (ret_val > 1){
printk("Led GPIO already set.\n");
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return (0);
error:
DBGPRINTF_E("Invalid arguments to led command\n");
return(-1);
}
/*
* GPIO programs - separate from the LED programs
*/
static u8 gpio_wps_push_button = 255;
static u8 wps_push_button_active_logic = 0;
static u8 wps_push_button_interrupts = 0;
static u8 wps_push_button_configured = 0;
static u8 wps_push_button_enabled = 0;
int
qdrv_get_wps_push_button_config( u8 *p_gpio_pin, u8 *p_use_interrupt, u8 *p_active_logic )
{
int retval = 0;
if (wps_push_button_configured != 0) {
*p_gpio_pin = gpio_wps_push_button;
*p_use_interrupt = wps_push_button_interrupts;
*p_active_logic = (wps_push_button_active_logic == 0) ? 0 : 1;
} else {
retval = -1;
}
return( retval );
}
void
set_wps_push_button_enabled( void )
{
wps_push_button_enabled = 1;
}
static int
qdrv_command_gpio(struct device *dev, int argc, char *argv[])
{
int retval = 0;
u8 gpio_pin = 0;
unsigned int tmp_uval = 0;
int wps_flag = 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (argc < 3)
{
DBGPRINTF_E("Not enough arguments to gpio command\n");
return(-1);
}
else
{
if (strcmp( argv[ 2 ], "wps" ) == 0)
wps_flag = 1;
else if (sscanf(argv[ 2 ] , "%u", &tmp_uval) != 1)
{
goto bad_args;
}
gpio_pin = (u8) tmp_uval;
}
if (strcmp( argv[ 1 ], "get" ) == 0)
{
if (wps_flag == 0)
{
retval = -1;
}
/*
* For WPS, just report thru printk.
* No reporting thru /proc/qdrvdata (qdrv_control_set_show, etc.)
*/
else
{
if (wps_push_button_configured)
{
printk( "WPS push button accessed using GPIO pin %u\n", gpio_wps_push_button );
printk( "monitored using %s\n", wps_push_button_interrupts ? "interrupt" : "polling" );
}
else
{
printk( "WPS push button not configured\n" );
}
}
}
else if (strcmp( argv[ 1 ], "set" ) == 0)
{
if (argc < 4)
{
DBGPRINTF_E("Not enough arguments for gpio set command\n");
retval = -1;
}
else if (wps_flag == 0)
{
retval = -1;
}
/*
* For WPS, we have "gpio set wps 4" and "gpio set wps 4 intr".
* Latter selects interrupt-based monitoring.
*/
else if (wps_push_button_enabled == 0)
{
unsigned int tmp_uval_2 = 0;
if (sscanf(argv[ 3 ] , "%u", &tmp_uval) != 1)
{
goto bad_args;
}
wps_push_button_interrupts = 0;
wps_push_button_active_logic = 1;
if (argc > 4)
{
if (strcmp( argv[ 4 ], "intr" ) == 0)
{
wps_push_button_interrupts = 1;
wps_push_button_active_logic = 1;
}
else if (sscanf(argv[ 4 ], "%u", &tmp_uval_2 ) == 1)
{
wps_push_button_active_logic = (u8) tmp_uval_2;
}
}
if ((wps_push_button_interrupts && tmp_uval > MAX_GPIO_INTR) ||
(tmp_uval > MAX_GPIO_PIN))
{
printk( "GPIO pin number %u out of range, maximum is %d\n", tmp_uval,
wps_push_button_interrupts ? MAX_GPIO_INTR : MAX_GPIO_PIN );
goto bad_args;
}
else
{
gpio_wps_push_button = (u8) tmp_uval;
wps_push_button_configured = 1;
}
}
else
{
DBGPRINTF_E("WPS Push button enabled, cannot (re)configure.\n");
}
}
else
{
DBGPRINTF_E("Unrecognized gpio subcommand %s\n", argv[1]);
retval = -1;
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return( retval );
bad_args:
DBGPRINTF_E("Invalid argument(s) to gpio command\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
static int
qdrv_command_pwm(struct device *dev, int argc, char *argv[])
{
int retval = 0;
int pin = 0;
int high_count = 0;
int low_count = 0;
if (argc < 3)
goto bad_args;
if (sscanf(argv[2], "%d", &pin) != 1)
goto bad_args;
if (strcmp(argv[1], "enable") == 0) {
if (argc < 5)
goto bad_args;
if (sscanf(argv[3], "%d", &high_count) != 1)
goto bad_args;
if (sscanf(argv[4], "%d", &low_count) != 1)
goto bad_args;
retval = gpio_enable_pwm(pin, high_count - 1, low_count - 1);
} else if (strcmp(argv[1], "disable") == 0) {
retval = gpio_disable_pwm(pin);
} else {
goto bad_args;
}
return ( retval );
bad_args:
DBGPRINTF_E("Invalid argument(s) to pwm command\n");
DBGPRINTF_E("usage: %s (enable|disable) <pin> <high_count> <low_count>\n", argv[0]);
return (-1);
}
static void
qdrv_calcmd_show_packet_counts( struct seq_file *s, void *data, u32 num )
{
struct qdrv_packet_report *p_packet_report = (struct qdrv_packet_report *) data;
seq_printf( s, "RF1_TX = %d, RF2_TX = %d\n", p_packet_report->rf1.num_tx, p_packet_report->rf2.num_tx );
seq_printf( s, "RF1_RX = %d, RF2_RX = %d\n", p_packet_report->rf1.num_rx, p_packet_report->rf2.num_rx );
}
static void
qdrv_calcmd_show_tx_power( struct seq_file *s, void *data, u32 num )
{
unsigned int *p_data = (unsigned int *) data;
seq_printf( s, "%u %u %u %u\n", p_data[0], p_data[1], p_data[2], p_data[3] );
}
static void
qdrv_calcmd_show_rssi( struct seq_file *s, void *data, u32 num )
{
unsigned int *p_data = (unsigned int *) data;
seq_printf( s, "%d %d %d %d\n", p_data[0], p_data[1], p_data[2], p_data[3] );
}
static void
qdrv_calcmd_show_test_mode_param( struct seq_file *s, void *data, u32 num )
{
struct qdrv_cal_test_setting *cal_test_setting= (struct qdrv_cal_test_setting*) data;
seq_printf( s, "%d %d %d %d %d %d\n",
cal_test_setting->antenna,
cal_test_setting->mcs,
cal_test_setting->bw_set,
cal_test_setting->pkt_len,
cal_test_setting->is_eleven_N,
cal_test_setting->bf_factor_set
);
}
static void
qdrv_calcmd_show_vpd( struct seq_file *s, void *data, u32 num )
{
unsigned int *p_data = (unsigned int *) data;
seq_printf( s, "%d\n", p_data[0] );
}
static void
qdrv_calcmd_show_temperature( struct seq_file *s, void *data, u32 num )
{
struct qdrv_cb *qcb = (struct qdrv_cb *) data;
seq_printf( s, "%d %d\n", qcb->temperature_rfic_external, qcb->temperature_rfic_internal);
}
/* RF register value is now returned from the MuC */
static void
qdrv_show_rfmem(struct seq_file *s, void *data, u32 num)
{
struct qdrv_cb *qcb = (struct qdrv_cb *) data;
seq_printf(s, "mem[0x%08x] = 0x%08x\n", qcb->read_addr, qcb->rf_reg_val );
}
#ifdef POST_RF_LOOP
static void
qdrv_calcmd_post_rfloop_show(struct seq_file *s, void *data, u32 num)
{
int *p_data = (int *)data;
seq_printf(s, "%d\n", *p_data);
}
#endif
static void
qdrv_calcmd_show_pd_voltage_level(struct seq_file *s, void *data, u32 num)
{
int *p_data = (int *) data;
seq_printf(s, "%d %d %d %d\n", p_data[0], p_data[1], p_data[2], p_data[3]);
}
char dc_iq_calfile_version[VERSION_SIZE];
char power_calfile_version[VERSION_SIZE];
/*
* Calcmd format:
*
* Each calcmd is a sequence of U8's; thus each element is in the range 0 - 255.
*
* The first element identifies the calcmd. Look in macfw/cal/utils/common/calcmd.h for symbolic
* enums. Example values include SET_TEST_MODE and GET_TEST_STATS. Symbolic values are not used in
* the Q driver.
*
* Second element is required to be 0.
*
* Third element is the total length of the calcmd sequence. If no additional argument are present,
* this element will be 4. For each argument, add 2 to this element.
*
* Fourth element is also required to be 0.
*
* Remaining elements are the arguments, organized as pairs. First element in each pair is the
* argument index, numbered starting from 1 - NOT 0. Second element is the argument value. Thus
* ALL calcmd input arguments (output arguments are also possible) are required to be 8 bit values.
* Larger values (e.g. U16 or U32) have to be passed in 8-bit pieces, with Linux taking the value
* apart and the MuC reassembling then.
*
* Output arguments are returned in the same buffer used to send the calcmd to the MuC. See
* GET_TEST_STATS (calcmd = 15) for an example.
*/
static int qdrv_command_calcmd(struct device *dev, int argc, char *argv[])
{
struct qdrv_cb *qcb;
char *cmd = NULL;
dma_addr_t cmd_dma;
struct qdrv_wlan *qw;
int cmdlen;
int temp_calcmd[30] = {0};
char calcmd[30] = {0};
int i;
int evm_int[4] = {0}, evm_frac[4] = {0};
u32 num_rf1_rx;
u32 num_rf1_tx;
u32 num_rf2_rx;
u32 num_rf2_tx;
qcb = dev_get_drvdata(dev);
qw = qdrv_control_wlan_get(&qcb->macs[0]);
if (!qw) {
return -1;
}
cmd = qdrv_hostlink_alloc_coherent(NULL, sizeof(qcb->command), &cmd_dma, GFP_ATOMIC);
if (cmd == NULL) {
DBGPRINTF_E("Failed allocate %d bytes for cmd\n", sizeof(qcb->command));
return -1;
}
cmdlen = argc - 1;
for (i = 1; i < argc; i++) {
temp_calcmd[i-1] = _atoi(argv[i]);
calcmd[i-1] = (char)temp_calcmd[i-1];
}
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_CALCMD, "cmdlen %d\n", cmdlen);
memcpy(cmd, calcmd, cmdlen);
qdrv_hostlink_msg_calcmd(qw, cmdlen, cmd_dma);
if(cmd[0] == 31) {
sprintf(dc_iq_calfile_version, "V%d.%d", cmd[6], cmd[5]);
sprintf(power_calfile_version, "V%d.%d", cmd[9], cmd[8]);
DBGPRINTF(DBG_LL_INFO, QDRV_LF_CALCMD,
"Calibration version %d.%d\n", cmd[12], cmd[11]);
DBGPRINTF(DBG_LL_INFO, QDRV_LF_CALCMD,
"RFIC version %d.%d\n", cmd[15], cmd[14]);
DBGPRINTF(DBG_LL_INFO, QDRV_LF_CALCMD,
"BBIC version %d.%d\n", cmd[18], cmd[17]);
} else if(cmd[0] == 28 || cmd[0] == 29 || cmd[0] == 30) {
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD, ".");
} else if(cmd[0] == 15) {
num_rf1_rx = cmd[8] << 24 | cmd[7] << 16 | cmd[6] << 8 | cmd[5];
num_rf1_tx = cmd[13] << 24 | cmd[12] << 16 | cmd[11] << 8 | cmd[10];
num_rf2_rx = cmd[18] << 24 | cmd[17] << 16 | cmd[16] << 8 | cmd[15];
num_rf2_tx = cmd[23] << 24 | cmd[22] << 16 | cmd[21] << 8 | cmd[20];
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"MuC: RF1_TX = %d, RF2_TX = %d\n", num_rf1_tx, num_rf2_tx);
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"MuC: RF1_RX = %d, RF2_RX = %d\n", num_rf1_rx, num_rf2_rx);
qcb->packet_report.rf1.num_tx = num_rf1_tx;
qcb->packet_report.rf2.num_tx = num_rf2_tx;
if (chip_id() == 0x20) {
qcb->packet_report.rf1.num_rx += num_rf1_rx;
qcb->packet_report.rf2.num_rx += num_rf2_rx;
} else {
qcb->packet_report.rf1.num_rx = num_rf1_rx;
qcb->packet_report.rf2.num_rx = num_rf2_rx;
}
qdrv_control_set_show(qdrv_calcmd_show_packet_counts, (void *) &(qcb->packet_report), 1, 1);
} else if(cmd[0] == 3) {
int temp_cal_13_W = 0, temp_cal_13_I, temp_cal_13_P;
u32 rfic_temp_int;
u32 rfic_temp_frac;
u32 flag = (cmd[8] << 24 | cmd[7] << 16 | cmd[6] << 8 | cmd[5]);
u32 rfic_temp = (cmd[28] << 24 | cmd[27] << 16 | cmd[26] << 8 | cmd[25]);
qtn_tsensor_get_temperature(qw->se95_temp_sensor, &temp_cal_13_W);
temp_cal_13_I = (int) (temp_cal_13_W / QDRV_TEMPSENS_COEFF);
temp_cal_13_P = ABS((temp_cal_13_W - (temp_cal_13_I * QDRV_TEMPSENS_COEFF)));
rfic_temp_int = rfic_temp / QDRV_TEMPSENS_COEFF10;
rfic_temp_frac = rfic_temp / QDRV_TEMPSENS_COEFF -
(rfic_temp_int * (QDRV_TEMPSENS_COEFF10/QDRV_TEMPSENS_COEFF));
if (flag == EXT_TEMPERATURE_SENSOR_REPORT_FLAG) {
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"Gain = %d, %d, %d, %d\n",
(cmd[15] | cmd[16] << 8), (cmd[17] | cmd[18] << 8),
(cmd[20] | cmd[21] << 8), (cmd[22] | cmd[23] << 8));
} else if (flag == DISABLE_REPORT_FLAG) {
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD, "Power compensation is Disabled\n");
} else {
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"(RF,BB) = (%d, %d), (%d, %d), (%d, %d), (%d, %d)\n",
cmd[5], cmd[10], cmd[6], cmd[11],
cmd[7], cmd[12], cmd[8], cmd[13]);
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
" Voltage = %d, %d, %d, %d\n",
(cmd[15] | cmd[16] << 8), (cmd[17] | cmd[18] << 8),
(cmd[20] | cmd[21] << 8), (cmd[22] | cmd[23] << 8));
}
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"TEMPERATURE_RFIC_EXTERNAL= %d.%d\n",
temp_cal_13_I, temp_cal_13_P);
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"TEMPERATURE_RFIC_INTERNAL = %d.%d\n",
rfic_temp_int, rfic_temp_frac); /* Please do not delete for future use */
qcb->temperature_rfic_external = temp_cal_13_W;
qcb->temperature_rfic_internal = rfic_temp;
qdrv_control_set_show(qdrv_calcmd_show_temperature, (void *)qcb, 1, 1);
} else if (cmd[0] == 12) /* SET_TEST_MODE */ {
qcb->packet_report.rf1.num_tx = 0;
qcb->packet_report.rf1.num_rx = 0;
qcb->packet_report.rf2.num_tx = 0;
qcb->packet_report.rf2.num_rx = 0;
} else if (cmd[0] == 33) /* GET_RFIC_REG */ {
u32 register_value = cmd[8] << 24 | cmd[7] << 16 | cmd[6] << 8 | cmd[5];
u32 register_address = cmd[13] << 24 | cmd[12] << 16 | cmd[11] << 8 | cmd[10];
qcb->read_addr = register_address;
qcb->rf_reg_val = register_value;
qdrv_control_set_show(qdrv_show_rfmem, (void *) qcb, 1, 1);
} else if(cmd[0] == 41) {
u8 mcs;
u16 rx_gain;
u16 evm[4];
u16 num_rx_sym;
u8 bw, nsts, format, rssi_flag;
int16_t rssi[4];
mcs = cmd[5];
rx_gain = cmd[8] << 8 | cmd[7];
num_rx_sym = cmd[21] << 8 | cmd[20];
bw = cmd[23];
nsts = cmd[25];
format = cmd[27];
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"MCS = %d, RX SYMBOL NUM = %d, NSTS = %d, BW = %d, FORMAT = %d _\n",