blob: fcd396433d441911433af0bd6417f41f6d54222f [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 "../../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",
mcs, num_rx_sym, nsts, bw, format);
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"RX_GAIN = %d __ (0x%x)\n", rx_gain, rx_gain);
rssi_flag = cmd[29];
if(rssi_flag > 0) //rssi_flag is 0 for EVM measurement
{
rssi[0] = (int16_t)(cmd[11] << 8 | cmd[10]);
rssi[1] = (int16_t)(cmd[13] << 8 | cmd[12]);
rssi[2] = (int16_t)(cmd[16] << 8 | cmd[15]);
rssi[3] = (int16_t)(cmd[18] << 8 | cmd[17]);
if(rssi_flag == 1)
DBGPRINTF(DBG_LL_HIDDEN, QDRV_LF_CALCMD,
"RX_RSSI (dBFS) : %d.%d, %d.%d, %d.%d, %d.%d\n",
rssi[0] / 10, ABS(rssi[0]) % 10 , rssi[1] / 10, ABS(rssi[1]) % 10,
rssi[2] / 10, ABS(rssi[2]) % 10 , rssi[3] / 10, ABS(rssi[3]) % 10);
else
DBGPRINTF(DBG_LL_HIDDEN, QDRV_LF_CALCMD,
"RX_RSSI (dBm) : %d.%d, %d.%d, %d.%d, %d.%d\n",
rssi[0] / 10, ABS(rssi[0]) % 10 , rssi[1] / 10, ABS(rssi[1]) % 10,
rssi[2] / 10, ABS(rssi[2]) % 10 , rssi[3] / 10, ABS(rssi[3]) % 10);
}
else
{
evm[0] = cmd[11] << 8 | cmd[10];
evm[1] = cmd[13] << 8 | cmd[12];
evm[2] = cmd[16] << 8 | cmd[15];
evm[3] = cmd[18] << 8 | cmd[17];
if (evm[0] > 0) convert_evm_db(evm[0], num_rx_sym, &evm_int[0], &evm_frac[0]);
if (evm[1] > 0) convert_evm_db(evm[1], num_rx_sym, &evm_int[1], &evm_frac[1]);
if (evm[2] > 0) convert_evm_db(evm[2], num_rx_sym, &evm_int[2], &evm_frac[2]);
if (evm[3] > 0) convert_evm_db(evm[3], num_rx_sym, &evm_int[3], &evm_frac[3]);
DBGPRINTF(DBG_LL_HIDDEN, QDRV_LF_CALCMD,
"RX_EVM[0] = %d.%d RX_EVM[1] = %d.%d RX_EVM[2] = %d.%d RX_EVM[3] = %d.%d \n",
evm_int[0], evm_frac[0], evm_int[1], evm_frac[1],
evm_int[2], evm_frac[2], evm_int[3], evm_frac[3]);
}
} else if(cmd[0] == 48) {
s16 pd_vol0_reading = cmd[6] << 8 | cmd[5];
if (pd_vol0_reading > 511) pd_vol0_reading -= 1024;
s16 pd_vol1_reading = cmd[9] << 8 | cmd[8];
if (pd_vol1_reading > 511) pd_vol1_reading -= 1024;
s16 pd_vol2_reading = cmd[12] << 8 | cmd[11];
if (pd_vol2_reading > 511) pd_vol2_reading -= 1024;
s16 pd_vol3_reading = cmd[15] << 8 | cmd[14];
if (pd_vol3_reading > 511) pd_vol3_reading -= 1024;
s16 rfic_temp_reading = cmd[18] << 8 | cmd[17]; //need to check specs of RFIC4 to find out dynamic range temp sensor
u8 pd_dBm0 = cmd[20];
u8 pd_dBm1 = cmd[22];
u8 pd_dBm2 = cmd[24];
u8 pd_dBm3 = cmd[26];
printk("OUT PD_LEVEL : (%d, %d, %d, %d) / RFIC_TEMP = %d oC\n",
pd_vol0_reading, pd_vol1_reading, pd_vol2_reading,
pd_vol3_reading, rfic_temp_reading);
printk("OUT PD_POWER : %d.%ddBm, %d.%ddBm, %d.%ddBm, %d.%ddBm\n",
pd_dBm0 >> 2, (pd_dBm0 % 4) * 25,
pd_dBm1 >> 2, (pd_dBm1 % 4) * 25,
pd_dBm2 >> 2, (pd_dBm2 % 4) * 25,
pd_dBm3 >> 2, (pd_dBm3 % 4) * 25);
qcb->qdrv_cal_test_report.pd_voltage_level[0] = pd_vol0_reading;
qcb->qdrv_cal_test_report.pd_voltage_level[1] = pd_vol1_reading;
qcb->qdrv_cal_test_report.pd_voltage_level[2] = pd_vol2_reading;
qcb->qdrv_cal_test_report.pd_voltage_level[3] = pd_vol3_reading;
qdrv_control_set_show(qdrv_calcmd_show_pd_voltage_level, (void *) &(qcb->qdrv_cal_test_report.pd_voltage_level[0]), 1, 1);
} else if (cmd[0] == 51) {
u16 pd_vol0 = cmd[6] << 8 | cmd[5];
u16 pd_vol1 = cmd[9] << 8 | cmd[8];
u16 pd_vol2 = cmd[12] << 8 | cmd[11];
u16 pd_vol3 = cmd[15] << 8 | cmd[14];
printk("BASE PD_POWER : %d.%ddBm, %d.%ddBm, %d.%ddBm, %d.%ddBm\n",
pd_vol0 >> 2, (pd_vol0 % 4) * 25,
pd_vol1 >> 2, (pd_vol1 % 4) * 25,
pd_vol2 >> 2, (pd_vol2 % 4) * 25,
pd_vol3 >> 2, (pd_vol3 % 4) * 25);
qcb->qdrv_cal_test_report.tx_power[0] = pd_vol0;
qcb->qdrv_cal_test_report.tx_power[1] = pd_vol1;
qcb->qdrv_cal_test_report.tx_power[2] = pd_vol2;
qcb->qdrv_cal_test_report.tx_power[3] = pd_vol3;
qdrv_control_set_show(qdrv_calcmd_show_tx_power, (void *) &(qcb->qdrv_cal_test_report.tx_power[0]), 1, 1);
}
else if(cmd[0] == 54)
{
int rssi[4];
rssi[0] = cmd[8] << 24 | cmd[7] << 16 | cmd[6] << 8 | cmd[5];
rssi[0] = ((rssi[0] > 0xFFF) ? 0xFFF : rssi[0]);
rssi[1] = cmd[13] << 24 | cmd[12] << 16 | cmd[11] << 8 | cmd[10];
rssi[1] = ((rssi[1] > 0xFFF) ? 0xFFF : rssi[1]);
rssi[2] = cmd[18] << 24 | cmd[17] << 16 | cmd[16] << 8 | cmd[15];
rssi[2] = ((rssi[2] > 0xFFF) ? 0xFFF : rssi[2]);
rssi[3] = cmd[23] << 24 | cmd[22] << 16 | cmd[21] << 8 | cmd[20];
rssi[3] = ((rssi[3] > 0xFFF) ? 0xFFF : rssi[3]);
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"RSSI (dBm) : %d.%d, %d.%d, %d.%d, %d.%d\n",
rssi[0] / 10, ABS(rssi[0]) % 10, rssi[1] / 10, ABS(rssi[1]) % 10,
rssi[2] / 10, ABS(rssi[2]) % 10, rssi[3] / 10, ABS(rssi[3]) % 10);
qcb->qdrv_cal_test_report.rssi[0] = rssi[0];
qcb->qdrv_cal_test_report.rssi[1] = rssi[1];
qcb->qdrv_cal_test_report.rssi[2] = rssi[2];
qcb->qdrv_cal_test_report.rssi[3] = rssi[3];
qdrv_control_set_show(qdrv_calcmd_show_rssi, (void *) &(qcb->qdrv_cal_test_report.rssi[0]), 1, 1);
}
else if (cmd[0] == 56) {
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"Get Test Mode = Ant_Sel: %d, MCS: %d, BW: %d, Pkt_Len: %d, Protocol: %d, BF: %d\n",
cmd[5], cmd[7], cmd[9], cmd[11], cmd[13], cmd[15]);
qcb->qdrv_cal_test_report.setting.antenna = cmd[5];
qcb->qdrv_cal_test_report.setting.mcs = cmd[7];
qcb->qdrv_cal_test_report.setting.bw_set = cmd[9];
qcb->qdrv_cal_test_report.setting.pkt_len = cmd[11];
qcb->qdrv_cal_test_report.setting.is_eleven_N = cmd[13];
qcb->qdrv_cal_test_report.setting.bf_factor_set = cmd[15];
qdrv_control_set_show(qdrv_calcmd_show_test_mode_param, (void *) &qcb->qdrv_cal_test_report.setting, 1, 1);
}
#ifdef POST_RF_LOOP
else if (cmd[0] == 60) {
qcb->qdrv_cal_test_report.post_rfloop_success = cmd[5];
qdrv_control_set_show(qdrv_calcmd_post_rfloop_show, (void *)&qcb->qdrv_cal_test_report.post_rfloop_success, 1, 1);
}
#endif
else if (cmd[0] == 62) {
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD, "Calstate TX Power = %d\n", cmd[5]);
qcb->calstate_vpd = cmd[5];
qdrv_control_set_show(qdrv_calcmd_show_vpd, (void *) &qcb->calstate_vpd, 1, 1);
}
else if (cmd[0] == 63) {
/* Rx IQ cal cmd: print failed status*/
if(cmd[5] > 0)
DBGPRINTF_RAW(DBG_LL_INFO, QDRV_LF_CALCMD,
"qdrv ERROR: cmd id 63 failed: status= %d\n", cmd[5]);
}
qdrv_hostlink_free_coherent(NULL, sizeof(qcb->command), cmd, cmd_dma);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}
int qdrv_command_read_rf_reg(struct device *dev, int offset)
{
struct qdrv_cb *qcb;
char *cmd = NULL;
dma_addr_t cmd_dma;
struct qdrv_wlan *qw;
int cmdlen;
char calcmd[8];
int result = 0;
qcb = dev_get_drvdata(dev);
qw = (struct qdrv_wlan *)qcb->macs[0].data;
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 = sizeof(calcmd)/sizeof(calcmd[0]);
calcmd[0] = 33; //GET_RFIC_REG;
calcmd[1] = 0;
calcmd[2] = cmdlen;
calcmd[3] = 0;
calcmd[4] = 1;
calcmd[5] = 0;
calcmd[6] = 2;
calcmd[7] = offset;
memcpy(cmd, calcmd, cmdlen);
qdrv_hostlink_msg_calcmd(qw, cmdlen, cmd_dma);
result = (cmd[8] << 24 | cmd[7] << 16 | cmd[6] << 8 | cmd[5]);
qdrv_hostlink_free_coherent(NULL, sizeof(qcb->command), cmd, cmd_dma);
return result;
}
int qdrv_command_read_chip_ver(struct device *dev)
{
struct qdrv_cb *qcb;
char *cmd = NULL;
dma_addr_t cmd_dma;
struct qdrv_wlan *qw;
int cmdlen, i;
char calcmd[6];
char ret_val;
for(i = 0; i < 6; i++)
calcmd[i] = 0;
/* Get the private device data */
qcb = dev_get_drvdata(dev);
qw = (struct qdrv_wlan *)qcb->macs[0].data;
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 = 4; //Cmd format : 11 0 4 0 //
calcmd[0] = GET_CHIP_ID;
calcmd[1] = 0;
calcmd[2] = cmdlen;
calcmd[3] = 0;
memcpy(cmd, calcmd, cmdlen);
qdrv_hostlink_msg_calcmd(qw, cmdlen, cmd_dma);
ret_val = cmd[5];
qdrv_hostlink_free_coherent(NULL, sizeof(qcb->command), cmd, cmd_dma);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return ret_val;
}
void qdrv_calcmd_set_tx_power(struct device *dev, uint8_t value)
{
struct qdrv_cb *qcb;
char *cmd = NULL;
dma_addr_t cmd_dma;
struct qdrv_wlan *qw;
qcb = dev_get_drvdata(dev);
qw = (struct qdrv_wlan *)qcb->macs[0].data;
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;
}
cmd[0] = 19;
cmd[1] = 0;
cmd[2] = VERIWAVE_TXPOWER_CMD_SIZE;
cmd[3] = 0;
cmd[4] = 1;
if (!value)
value = 11;
cmd[5] = 4 * value;
qdrv_hostlink_msg_calcmd(qw, VERIWAVE_TXPOWER_CMD_SIZE, cmd_dma);
qdrv_hostlink_free_coherent(NULL, sizeof(qcb->command), cmd, cmd_dma);
}
static int qdrv_command_write(struct device *dev, int argc, char *argv[])
{
u32 addr;
u32 value;
u32 *segvaddr;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Check that we have all the arguments */
if(argc != 4) {
goto error;
}
if(strcmp(argv[1], "addr") == 0) {
if (sscanf(argv[2], "%x", &addr) != 1) {
goto error;
}
if (sscanf(argv[3], "%x", &value) != 1) {
goto error;
}
} else {
goto error;
}
/* Check that address is valid */
if (!qdrv_command_is_valid_addr(addr)) {
DBGPRINTF_E("addr 0x%x is not valid\n", (unsigned)addr);
goto error;
}
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL, "0x%08x = 0x%08x\n", addr, value);
segvaddr = ioremap_nocache(addr, 4);
if (segvaddr == NULL) {
goto error;
}
*segvaddr = value;
iounmap(segvaddr);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
error:
DBGPRINTF_E("Invalid arguments to write command\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
static int qdrv_command_read(struct device *dev, int argc, char *argv[])
{
u32 addr;
unsigned int num;
int values_per_line = 1;
struct qdrv_cb *qcb = dev_get_drvdata(dev);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if(argc < 4) {
goto error;
}
if(strcmp(argv[1], "addr") == 0) {
if (sscanf(argv[2], "%x", &addr) != 1) {
goto error;
}
if (sscanf(argv[3], "%u", &num) != 1) {
goto error;
}
qcb->read_count = num;
if (argc > 4) {
if (sscanf(argv[4], "%d", &values_per_line) != 1) {
goto error;
}
if (values_per_line != 1 &&
values_per_line != 2 &&
values_per_line != 4) {
goto error;
}
num = (num + values_per_line - 1) / values_per_line;
}
} else {
goto error;
}
DBGPRINTF(DBG_LL_DEBUG, QDRV_LF_QCTRL, "0x%08x (%d)\n", addr, num);
qdrv_control_set_show(qdrv_show_memory, (void *) qcb, num, 1);
/* Save the address for a memory read */
qcb->read_addr = addr;
qcb->values_per_line = values_per_line;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
error:
DBGPRINTF_E("Invalid arguments to read command\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
/*
* Push selected sysmsgs for capture and debugging
* - currently only MuC messages are sent
*
* Add a debug message to the output buffer.
* If the output buffer is full it is forwarded to the Ethernet driver.
* A timer function also calls this function periodically so that data is not left in the
* buffer for too long.
*/
void qdrv_control_sysmsg_send(void *data, char *sysmsg, u_int32_t text_len, int send_now)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) data;
struct qdrv_mac *mac = qw->mac;
uint64_t tsf;
#define QDRV_CMD_SYSMSG_PREF_LEN 25
char fmt[] = "[%08x.%08x] MuC: %s";
u_int16_t line_len = QDRV_CMD_SYSMSG_PREF_LEN + text_len;
static struct qdrv_netdebug_sysmsg *msgbuf = NULL;
static int msg_len = 0;
if (mac->params.mucdbg_netdbg == 0 &&
(qw->pktlogger.flag & BIT(QDRV_NETDEBUG_TYPE_SYSMSG)) == 0) {
return;
}
if ((msgbuf != NULL) &&
((send_now != 0) || ((msg_len + line_len) > (QDRV_NETDEBUG_SYSMSG_LENGTH - 20)))) {
int udp_len = sizeof(msgbuf->ndb_hdr) + msg_len + 1;
qdrv_pktlogger_hdr_init(qw, &msgbuf->ndb_hdr, QDRV_NETDEBUG_TYPE_SYSMSG, udp_len);
qdrv_pktlogger_send(msgbuf, udp_len);
msgbuf = NULL;
msg_len = 0;
}
if (text_len == 0 || sysmsg == NULL) {
return;
}
if (msgbuf == NULL) {
msgbuf = qdrv_pktlogger_alloc_buffer("sysmsg", sizeof(*msgbuf));
if (msgbuf == NULL) {
return;
}
}
if (mac->params.mucdbg_netdbg) {
qw->ic.ic_get_tsf(&tsf);
snprintf(msgbuf->msg + msg_len, line_len, fmt, U64_HIGH32(tsf), U64_LOW32(tsf), sysmsg),
msg_len += line_len;
} else if (qw->pktlogger.flag & BIT(QDRV_NETDEBUG_TYPE_SYSMSG)) {
sprintf(msgbuf->msg + msg_len, "%s", sysmsg);
msg_len += text_len;
}
}
/*
* Push TXBF pkt for capture and debugging
*/
void qdrv_control_txbf_pkt_send(void *data, u8 *stvec, u32 bw)
{
struct qdrv_wlan *qw = (struct qdrv_wlan *)data;
void *databuf = NULL;
struct qdrv_netdebug_txbf *stats;
int indx;
for (indx = 0; indx <= !!bw; indx++) {
databuf = qdrv_pktlogger_alloc_buffer("txbf", sizeof(*stats));
if (databuf == NULL) {
return;
}
stats = (struct qdrv_netdebug_txbf*)databuf;
qdrv_pktlogger_hdr_init(qw, &stats->ndb_hdr,
QDRV_NETDEBUG_TYPE_TXBF, sizeof(*stats));
if (bw && !indx) {
stats->ndb_hdr.flags = QDRV_NETDEBUG_FLAGS_TRUNCATED;
}
dma_map_single(NULL, stvec + (indx * QDRV_NETDEBUG_TXBF_DATALEN),
QDRV_NETDEBUG_TXBF_DATALEN, DMA_FROM_DEVICE);
memcpy(stats->stvec_data, stvec + (indx * QDRV_NETDEBUG_TXBF_DATALEN),
QDRV_NETDEBUG_TXBF_DATALEN);
qdrv_pktlogger_send(stats, sizeof(*stats));
databuf = NULL;
}
}
static void qdrv_command_memdebug_usage(void)
{
printk("Usage: \n"
"\tmemdebug 0 add <address> <size> - e.g. memdebug add 3e01430 16\n");
}
static int qdrv_command_memdebug(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac;
struct qdrv_wlan *qw;
size_t totalsize = 0;
int i;
struct qdrv_memdebug_watchpt *wp;
if (argc < 2) {
qdrv_command_memdebug_usage();
return -1;
}
mac = qdrv_control_mac_get(dev, argv[1]);
if (mac == NULL) {
return -1;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return -1;
}
if (strncmp(argv[2], "add", 3) == 0) {
if (argc < 4) {
qdrv_command_memdebug_usage();
return -1;
}
if (qw->pktlogger.mem_wp_index >= MAX_MEMDEBUG_WATCHPTS) {
DBGPRINTF_E("memdebug watchpoint limit reached\n");
return -1;
}
wp = &qw->pktlogger.mem_wps[qw->pktlogger.mem_wp_index];
if (sscanf(argv[3], "%lx", (unsigned long *)&wp->addr) != 1) {
DBGPRINTF_E("could not parse hex address\n");
return -1;
}
if ((wp->addr & 0x03)) {
DBGPRINTF_E("address must be word-aligned\n");
return -1;
}
if (sscanf(argv[4], "%lu", (unsigned long *)&wp->size) != 1) {
DBGPRINTF_E("could not parse decimal size\n");
return -1;
}
/*
* totalsize if the amount of payload:
* [wp struct] [data] * num watchpoints
* check that we're not requesting too much data, e.g. oversizing the debug packet
*/
for (i = 0; i <= qw->pktlogger.mem_wp_index; i++) {
totalsize += (qw->pktlogger.mem_wps[i].size * sizeof(u32));
totalsize += sizeof(struct qdrv_memdebug_watchpt);
}
if (totalsize > QDRV_NETDEBUG_MEM_DATALEN) {
DBGPRINTF_E("data monitoring packet limit hit\n");
return -1;
}
wp->remap_addr = ioremap_nocache(wp->addr, (wp->size * sizeof(u32)));
if (wp->remap_addr == NULL) {
DBGPRINTF_E("unable to remap address\n");
return -1;
}
printk("%s add %08x %p %u\n", __FUNCTION__, wp->addr, wp->remap_addr, wp->size);
qw->pktlogger.mem_wp_index++;
} else {
qdrv_command_memdebug_usage();
return -1;
}
return 0;
}
#ifdef QDRV_TX_DEBUG
__sram_text uint32_t qdrv_tx_ctr[60] = {0};
__sram_text uint32_t qdrv_dbg_ctr[8] = {0};
/*
* Display or enable Tx debugs
*
* Syntax:
* txdbg
* - display all qdrv_tx_ctr[] values
*
* txdbg [<ctr> <cnt>] ...
* - print the next <cnt> QDRV_TX_DBG(<ctr>, ...) debug messages
* - any number of <ctr>/<cnt> pairs may be specified
*/
static int qdrv_command_txdbg(struct device *dev, int argc, char *argv[])
{
int i;
int j;
if (argc > 2) {
printk("qdrv_dbg_ctr");
for (i = 1; (i + 1) < argc; i += 2) {
sscanf(argv[i], "%d", &j);
if (j > (ARRAY_SIZE(qdrv_dbg_ctr) - 1)) {
printk("%s: ctr %d exceeds array size\n",
argv[0], j);
}
sscanf(argv[i + 1], "%d", &qdrv_dbg_ctr[j]);
printk(" [%u]=%u", j, qdrv_dbg_ctr[j]);
}
printk("\n");
return 0;
}
for (i = 0; i < ARRAY_SIZE(qdrv_tx_ctr); i++) {
printk("%02u:%-8u ", i, qdrv_tx_ctr[i]);
if (((i + 1) % 12) == 0) {
printk("\n");
}
}
return 0;
}
#endif
static int qdrv_command_clearsram(struct device *dev, int argc, char *argv[])
{
struct qdrv_cb *qcb;
uint16_t *buf = (uint16_t *) (CONFIG_ARC_MUC_STACK_INIT - CONFIG_ARC_MUC_STACK_SIZE);
qcb = dev_get_drvdata(dev);
/* Copy out any crash log, let the parse function deal with validity of the buffer */
if (qdrv_crash_log == NULL) {
/*
* Format of buffer:
* 2 bytes: Header (HEADER_CORE_DUMP)
* 2 bytes: Length of the compressed logs (n)
* n bytes: Compressed logs
*/
/* Check if the header exists */
if (*buf == HEADER_CORE_DUMP) {
uint32_t len = *(buf + 1);
if (len > CONFIG_ARC_MUC_STACK_SIZE) {
DBGPRINTF_E("%s: crash log len (%u) out of range\n", __func__, len);
return -1;
}
qdrv_crash_log = kmalloc(len, GFP_KERNEL);
if (!qdrv_crash_log) {
DBGPRINTF_E("%s: Could not allocate %u bytes for qdrv_crash_log\n", __func__,
len);
return -1;
}
/* Strip the header and length while copying */
memcpy(qdrv_crash_log, (char *) (buf + 2), len);
qdrv_crash_log_len = len;
}
}
if (qcb->resources == 0) {
/*
* Strictly speaking memory clearing is not necessary as during ELF segments copying
* to memory, it is cleared before placing data.
* Firmware should clear heaps by itself.
* But let's have this function (can be invoked from user-space only by writing
* command to sysfs file) to fill holes between segments, and for safety.
*/
u32 *p_uncached = ioremap_nocache(RUBY_SRAM_BEGIN + CONFIG_ARC_MUC_SRAM_B1_BASE, CONFIG_ARC_MUC_SRAM_B1_SIZE);
memset(p_uncached, 0, CONFIG_ARC_MUC_SRAM_B1_SIZE);
iounmap(p_uncached);
p_uncached = ioremap_nocache(RUBY_SRAM_BEGIN + CONFIG_ARC_MUC_SRAM_B2_BASE, CONFIG_ARC_MUC_SRAM_B2_SIZE);
memset(p_uncached, 0, CONFIG_ARC_MUC_SRAM_B2_SIZE);
iounmap(p_uncached);
p_uncached = ioremap_nocache(RUBY_CRUMBS_ADDR, RUBY_CRUMBS_SIZE);
memset(p_uncached, 0, RUBY_CRUMBS_SIZE);
iounmap(p_uncached);
p_uncached = ioremap_nocache(RUBY_DRAM_BEGIN + CONFIG_ARC_MUC_BASE, CONFIG_ARC_MUC_SIZE);
memset(p_uncached, 0, CONFIG_ARC_MUC_SIZE);
iounmap(p_uncached);
p_uncached = ioremap_nocache(RUBY_DRAM_BEGIN + CONFIG_ARC_DSP_BASE, CONFIG_ARC_DSP_SIZE);
memset(p_uncached, 0, CONFIG_ARC_DSP_SIZE);
iounmap(p_uncached);
} else {
DBGPRINTF_E("Resources are held, not freeing SRAM\n");
}
return 0;
}
int qdrv_copy_core_dump(void *buf, uint32_t len, uint32_t *len_copied)
{
if (!buf || (len < qdrv_crash_log_len) || !len_copied)
return -EINVAL;
if (qdrv_crash_log_len)
memcpy(buf, qdrv_crash_log, qdrv_crash_log_len);
*len_copied = qdrv_crash_log_len;
return 0;
}
static int qdrv_command_bridge(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac;
struct qdrv_wlan *qw;
struct ieee80211vap *vap;
char *dev_name;
if (argc < 2) {
printk("Usage: bridge 0 {showmacs | enable | disable | clear}\n");
return -1;
}
mac = qdrv_control_mac_get(dev, argv[1]);
if (mac == NULL) {
return -1;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return -1;
}
if (mac->vnet[0] != NULL) {
dev_name = mac->vnet[0]->name;
} else {
/* Maybe never come here */
DBGPRINTF_E("No primary interface\n");
return -1;
}
vap = TAILQ_FIRST(&qw->ic.ic_vaps);
if (vap->iv_opmode != IEEE80211_M_STA) {
DBGPRINTF_E("%s: 3-address mode bridging is only supported on stations\n",
dev_name);
return 0;
}
if (strncmp(argv[2], "showmacs", 8) == 0) {
if (!QDRV_FLAG_3ADDR_BRIDGE_ENABLED()) {
printk("%s: 3-address mode bridging is disabled\n",
dev_name);
return 0;
}
qdrv_br_show(&qw->bridge_table);
} else if (strncmp(argv[2], "enable", 6) == 0) {
if (QDRV_FLAG_3ADDR_BRIDGE_ENABLED()) {
printk("%s: 3-address mode bridging is already enabled\n",
dev_name);
return 0;
}
qdrv_br_create(&qw->bridge_table);
qw->flags_ext &= ~QDRV_FLAG_3ADDR_BRIDGE_DISABLE;
printk("%s: 3-address mode bridging enabled \n",
dev_name);
} else if (strncmp(argv[2], "disable", 7) == 0) {
if (!QDRV_FLAG_3ADDR_BRIDGE_ENABLED()) {
printk("%s: 3-address mode bridging is already disabled\n",
dev_name);
return 0;
}
qw->flags_ext |= QDRV_FLAG_3ADDR_BRIDGE_DISABLE;
qdrv_br_delete(&qw->bridge_table);
printk("%s: 3-address mode bridging disabled\n",
dev_name);
} else if (strncmp(argv[2], "clear", 5) == 0) {
if (!QDRV_FLAG_3ADDR_BRIDGE_ENABLED()) {
printk("%s: 3-address mode bridging is disabled\n",
dev_name);
return 0;
}
qdrv_br_clear(&qw->bridge_table);
} else {
printk("Usage: bridge 0 {showmacs | enable | disable | clear}\n");
}
return 0;
}
static inline void qdrv_control_show_wmm_ac_map(
struct seq_file *s, void *data, u32 num)
{
uint32_t i;
seq_printf(s, "TOS/AC:\n");
for (i = 0; i < IEEE8021P_PRIORITY_NUM; i++) {
seq_printf(s, "%d/%s\n", i, qdrv_sch_tos2ac_str(i));
}
}
static inline void qdrv_control_set_wmm_ac_map(
char *dev_name, int tos, int aid)
{
struct net_device *ndev = dev_get_by_name(&init_net, dev_name);
if (ndev) {
netif_stop_queue(ndev);
qdrv_sch_set_ac_map(tos, aid);
netif_start_queue(ndev);
dev_put(ndev);
} else {
printk("Fail to set wmm ac map, device can't be found.\n");
}
}
static int qdrv_control_set_br_isolate(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac = NULL;
struct qdrv_wlan *qw = NULL;
int val;
mac = qdrv_control_mac_get(dev, "0");
if (mac)
qw = qdrv_control_wlan_get(mac);
if (!qw)
return -ENODEV;
if (argc == 1) {
val = simple_strtol(argv[0], NULL, 10);
if (val < 0)
return -EINVAL;
else if (val > 0)
qw->br_isolate |= QDRV_BR_ISOLATE_NORMAL;
else
qw->br_isolate &= ~QDRV_BR_ISOLATE_NORMAL;
return 0;
}
if (argc == 2) {
if (!strcmp(argv[0], "vlan")) {
if (!strcmp(argv[1], "all")) {
qw->br_isolate |= QDRV_BR_ISOLATE_VLAN;
qw->br_isolate_vid = QVLAN_VID_ALL;
return 0;
} else if (!strcmp(argv[1], "none")) {
qw->br_isolate &= ~QDRV_BR_ISOLATE_VLAN;
return 0;
}
val = simple_strtol(argv[1], NULL, 10);
if (val <= 0 || val >= QVLAN_VID_MAX)
return -EINVAL;
qw->br_isolate |= QDRV_BR_ISOLATE_VLAN;
qw->br_isolate_vid = val;
return 0;
}
}
return -EINVAL;
}
static int qdrv_control_set_sta_vlan(struct qdrv_mac *mac, const char *addr, uint16_t vid)
{
struct qdrv_wlan *qw = qdrv_mac_get_wlan(mac);
struct ieee80211com *ic = &qw->ic;
struct ieee80211_node_table *nt = &ic->ic_sta;
struct ieee80211_node *ni;
struct qdrv_vap *qv;
int ret;
uint8_t sta_addr[ETH_ALEN];
struct qtn_vlan_dev *vdev;
sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&sta_addr[0], &sta_addr[1], &sta_addr[2], &sta_addr[3], &sta_addr[4], &sta_addr[5]);
ni = ieee80211_find_node(nt, sta_addr);
if (unlikely(!ni)) {
printk(KERN_ERR"station %s was not found\n", addr);
return -EINVAL;
}
qv = container_of(ni->ni_vap, struct qdrv_vap, iv);
vdev = vdev_tbl_lhost[QDRV_WLANID_FROM_DEVID(qv->devid)];
ret = switch_vlan_set_node(vdev, IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), vid);
if (ret)
printk(KERN_ERR "failed to put station %s into VLAN %u\n", addr, vid);
else
printk(KERN_INFO"station %s into VLAN %u\n", addr, vid);
ieee80211_free_node(ni);
return ret;
}
static void qdrv_control_vlan_enable(struct qdrv_mac *mac, int enable)
{
struct qdrv_wlan *qw;
if (vlan_enabled != enable) {
vlan_enabled = enable;
#if !defined(CONFIG_TOPAZ_PCIE_HOST) && !defined(CONFIG_TOPAZ_PCIE_TARGET)
topaz_emac_to_lhost(enable);
#endif
qw = qdrv_mac_get_wlan(mac);
qdrv_wlan_vlan_enable(&qw->ic, enable);
}
}
int qdrv_control_set_vlan_enable(struct qdrv_mac *mac, const char *cmd)
{
if (strcmp(cmd, "enable") == 0) {
qdrv_control_vlan_enable(mac, 1);
} else if (strcmp(cmd, "disable") == 0) {
qdrv_control_vlan_enable(mac, 0);
switch_vlan_reset();
} else {
return -EINVAL;
}
return 0;
}
static int qdrv_control_vlan_config_cmd_parse(
int argc,
char *argv[],
int *vlanid,
int *tagtx,
int *pvid,
int *mode,
int *add)
{
if (!strcmp(argv[3], "access")) {
*mode = QVLAN_MODE_ACCESS;
} else if (!strcmp(argv[3], "trunk")) {
*mode = QVLAN_MODE_TRUNK;
} else if (!strcmp(argv[3], "hybrid")) {
*mode = QVLAN_MODE_HYBRID;
} else if (!strcmp(argv[3], "dynamic")) {
*mode = QVLAN_MODE_DYNAMIC;
*add = 1;
return 0;
} else if (!strcmp(argv[3], "undynamic")) {
*mode = QVLAN_MODE_DYNAMIC;
*add = 0;
return 0;
} else if (!strcmp(argv[3], "default_priority")) {
*mode = QVLAN_CMD_DEF_PRIORITY;
} else {
return -EINVAL;
}
*vlanid = simple_strtol(argv[4], NULL, 10);
if (*mode == QVLAN_CMD_DEF_PRIORITY)
return 0;
if (!strcmp(argv[5], "add"))
*add = 1;
else if (!strcmp(argv[5], "del"))
*add = 0;
else
return -EINVAL;
if (!strcmp(argv[6], "tag"))
*tagtx = 1;
else if (!strcmp(argv[6], "untag"))
*tagtx = 0;
else
return -EINVAL;
if (!strcmp(argv[7], "default"))
*pvid = 1;
else if (!strcmp(argv[7], "none"))
*pvid = 0;
else
return -EINVAL;
return 0;
}
static int qdrv_control_vlan_config_cmd_validation(
struct qtn_vlan_dev *vdev,
struct qdrv_mac *mac,
struct qdrv_vap *qv,
int wifidev,
int vlanid,
int tagtx,
int pvid,
int mode,
int add)
{
if (mode == QVLAN_MODE_DYNAMIC) {
if (!wifidev || qv->iv.iv_opmode != IEEE80211_M_HOSTAP) {
printk(KERN_ERR "Dynamic VLAN applies only to wifi AP interfaces\n");
return -EINVAL;
}
if (add == 1)
qdrv_control_vlan_enable(mac, 1);
return 0;
}
if (!vlan_enabled) {
printk(KERN_ERR "VLAN is disabled\n");
return -EINVAL;
}
if (mode == QVLAN_CMD_DEF_PRIORITY) {
if (vlanid <= QVLAN_PRIO_MAX)
return 0;
return -EINVAL;
}
if (vlanid != QVLAN_VID_ALL && !qtn_vlan_is_valid(vlanid))
return -EINVAL;
if (pvid == 1 && vlanid == QVLAN_VID_ALL)
return -EINVAL;
if (!qtn_vlan_is_mode(vdev, mode) && vlanid != QVLAN_PRIO_VID)
switch_vlan_dev_reset(vdev, mode);
if (add == 0 && !qtn_vlan_is_member(vdev, vlanid))
return -EINVAL;
if (add == 0 && pvid == 1 && !qtn_vlan_is_pvid(vdev, vlanid))
return -EINVAL;
if (qtn_vlan_is_pvid(vdev, vlanid) && pvid == 0)
return -EINVAL;
if (pvid == 1 && tagtx == 1)
return -EINVAL;
switch (mode) {
case QVLAN_MODE_ACCESS:
if (pvid == 0)
return -EINVAL;
break;
case QVLAN_MODE_TRUNK:
if (tagtx == 0 && pvid == 0)
return -EINVAL;
break;
case QVLAN_MODE_HYBRID:
default:
break;
}
return 0;
}
static int qdrv_control_vlan_config_cmd_execute(
struct qtn_vlan_dev *vdev,
int vlanid,
int tagtx,
int pvid,
int mode,
int add)
{
int ret = -1;
if (mode == QVLAN_MODE_DYNAMIC) {
if (add == 1) {
switch_vlan_dyn_enable(vdev);
return 0;
} else if (add == 0) {
if (QVLAN_IS_DYNAMIC(vdev))
switch_vlan_dyn_disable(vdev);
return 0;
} else {
return -EINVAL;
}
} else if (mode == QVLAN_CMD_DEF_PRIORITY) {
switch_vlan_set_priority(vdev, vlanid);
return 0;
}
if (pvid == 1 && add == 0) {
pvid = 1;
vlanid = QVLAN_DEF_PVID;
}
if (pvid == 1) {
ret = switch_vlan_set_pvid(vdev, vlanid);
} else if (add == 1) {
ret = switch_vlan_add_member(vdev, vlanid, 1);
if (ret == 0 && tagtx == 1) {
ret = switch_vlan_tag_member(vdev, vlanid);
} else if (ret == 0 && tagtx == 0) {
ret = switch_vlan_untag_member(vdev, vlanid);
}
} else if (add == 0) {
ret = switch_vlan_del_member(vdev, vlanid);
if (ret == 0 && (vlanid == QVLAN_VID_ALL ||
qtn_vlan_is_pvid(vdev, vlanid)))
ret = switch_vlan_set_pvid(vdev, QVLAN_DEF_PVID);
}
return ret;
}
static int qdrv_control_vlan_config(struct qdrv_mac *mac, int argc, char *argv[])
{
int vlanid = -1;
int ret;
int tagtx = -1;
int pvid = -1;
int mode = -1;
int add = -1;
struct net_device *ndev = NULL;
struct qdrv_vap *qv = NULL;
const char *dev_name = NULL;
struct qtn_vlan_dev *vdev;
int wifidev;
if (argc != 3 /* enable/disable */
&& argc != 4 /* reset */
&& argc != 5 /* priority_tag_tx/default_priority */
&& argc != 8) /* access/trunk/hybrid/dynamic */
return -EINVAL;
if (argc == 3)
return qdrv_control_set_vlan_enable(mac, argv[2]);
dev_name = argv[2];
ndev = dev_get_by_name(&init_net, dev_name);
if (!ndev) {
printk(KERN_ERR"%s: netdevice %s does not exist\n", __FUNCTION__, dev_name);
return -EINVAL;
}
wifidev = (ndev->qtn_flags & QTN_FLAG_WIFI_DEVICE);
dev_put(ndev);
if (wifidev) {
qv = netdev_priv(ndev);
vdev = switch_vlan_dev_get_by_idx(QTN_WLANID_FROM_DEVID(qv->devid));
} else {
vdev = switch_vlan_dev_get_by_port(ndev->if_port);
}
if (unlikely(vdev == NULL))
return -EINVAL;
if (argc == 4) {
if (strcmp(argv[3], "reset") != 0)
return -EINVAL;
switch_vlan_dev_reset(vdev, QVLAN_MODE_ACCESS);
return 0;
}
ret = qdrv_control_vlan_config_cmd_parse(argc, argv, &vlanid, &tagtx, &pvid, &mode, &add);
if (ret)
return ret;
ret = qdrv_control_vlan_config_cmd_validation(vdev, mac, qv, wifidev, vlanid, tagtx, pvid, mode, add);
if (ret)
return ret;
ret = qdrv_control_vlan_config_cmd_execute(vdev, vlanid, tagtx, pvid, mode, add);
if (ret)
return ret;
if (wifidev)
return qdrv_vap_vlan2index_sync(qv, mode, vlanid);
return 0;
}
static int qdrv_control_set_vlan_group(struct qdrv_mac *mac, const char *dev_name, uint16_t vid, int enable)
{
struct net_device *ndev;
struct qdrv_vap *qv;
struct qdrv_node *vlan_group;
ndev = dev_get_by_name(&init_net, dev_name);
if (!ndev) {
printk(KERN_ERR"netdevice %s does not exist\n", dev_name);
return -EINVAL;
}
qv = netdev_priv(ndev);
dev_put(ndev);
if (vid >= QVLAN_VID_MAX) {
printk(KERN_ERR"%u is not a valid VLAN\n", vid);
return -EINVAL;
}
if (enable) {
vlan_group = qdrv_vlan_find_group_noref(qv, vid);
if (vlan_group) {
printk(KERN_INFO"VLAN group %u is present\n", vid);
return 0;
}
vlan_group = qdrv_vlan_alloc_group(qv, vid);
if (!vlan_group) {
printk(KERN_INFO"VLAN group %u allocation failed\n", vid);
return -ENOMEM;
}
printk(KERN_INFO"VLAN group %u created\n", vid);
} else {
vlan_group = qdrv_vlan_find_group_noref(qv, vid);
if (!vlan_group) {
printk(KERN_INFO"VLAN group %u does not exist\n", vid);
return -EEXIST;
}
printk(KERN_INFO"VLAN group %u removed, refcnt %u\n", vid, ieee80211_node_refcnt(&vlan_group->qn_node));
qdrv_vlan_free_group(vlan_group); /* neutralize VLAN group creation */
}
return 0;
}
static int qdrv_control_mac_reserve(int argc, char *argv[])
{
uint8_t addr[IEEE80211_ADDR_LEN];
uint8_t mask[IEEE80211_ADDR_LEN];
if (argc == 0) {
qdrv_mac_reserve_clear();
return 0;
}
if (qdrv_parse_mac(argv[0], addr) < 0) {
printk("%s: invalid mac address %s\n", __func__, argv[0]);
return -1;
}
if (argc > 1) {
if (qdrv_parse_mac(argv[1], mask) < 0) {
printk("%s: invalid mask %s\n", __func__, argv[1]);
return -1;
}
} else {
memset(mask, 0xff, ARRAY_SIZE(mask));
}
return qdrv_mac_reserve_set(addr, mask);
}
static void qdrv_control_set_power_table_checksum(struct qdrv_cb *qcb, char *fname,
char *checksum)
{
struct qdrv_power_table_checksum_entry *p_entry;
struct qdrv_power_table_checksum_entry *n_entry;
if (qcb->power_table_ctrl.checksum_list_locked) {
printk("QDRV: power table checksum list has been locked\n");
return;
}
if (strlen(fname) > QDRV_POWER_TABLE_FNAME_MAX_LEN) {
printk("QDRV: power table filename is too long\n");
return;
}
if (strlen(checksum) != QDRV_POWER_TABLE_CHECKSUM_LEN) {
printk("QDRV: power table checksum length is invalid\n");
return;
}
p_entry = qcb->power_table_ctrl.checksum_list;
while (p_entry) {
if (strcmp(p_entry->fname, fname) == 0) {
printk("QDRV: power table checksum for %s exists\n", fname);
return;
}
if (p_entry->next) {
p_entry = p_entry->next;
} else {
break;
}
}
n_entry = kmalloc(sizeof(struct qdrv_power_table_checksum_entry), GFP_KERNEL);
if (!n_entry) {
printk("QDRV: set power table checksum malloc failed\n");
return;
}
n_entry->next = NULL;
strcpy(n_entry->fname, fname);
strcpy(n_entry->checksum, checksum);
if (p_entry) {
p_entry->next = n_entry;
} else {
qcb->power_table_ctrl.checksum_list = n_entry;
}
printk("QDRV: power table checksum for %s\n", fname);
}
static int qdrv_command_set(struct device *dev, int argc, char *argv[])
{
struct qdrv_cb *qcb;
struct qdrv_mac *mac = NULL;
struct qdrv_wlan *qw = NULL;
int i;
char *value;
char *name;
char *dest;
uint32_t vendor_fix;
uint32_t vap_default_state;
int32_t brcm_rxglitch_thrshlds;
qcb = dev_get_drvdata(dev);
name = argv[1];
if (name == NULL) {
DBGPRINTF_E("set command is NULL\n");
return -1;
}
value = argv[2];
if (value == NULL) {
DBGPRINTF_E("set command value for %s is NULL\n", name);
return -1;
}
if (strcmp(name, "wmm_ac_map") == 0) {
char *dev_name = argv[2];
int tos = simple_strtol(argv[3], NULL, 10);
int aid = simple_strtol(argv[4], NULL, 10);
qdrv_control_set_wmm_ac_map(dev_name, tos, aid);
return 0;
}
if (strcmp(name, "power_table_checksum") == 0) {
char *fname = argv[2];
char *checksum = argv[3];
qdrv_control_set_power_table_checksum(qcb, fname, checksum);
return 0;
}
if (strcmp(name, "lock_checksum_list") == 0) {
qcb->power_table_ctrl.checksum_list_locked = 1;
return 0;
}
if (strcmp(name, "power_selection") == 0) {
qcb->power_table_ctrl.power_selection = simple_strtol(argv[2], NULL, 10);
printk("set power_selection %u\n", qcb->power_table_ctrl.power_selection);
return 0;
}
if (strcmp(name, "power_recheck") == 0) {
qcb->power_table_ctrl.power_recheck = !!simple_strtol(argv[2], NULL, 10);
printk("set power_recheck %u\n", qcb->power_table_ctrl.power_recheck);
return 0;
}
if (strcmp(name, "vlan") == 0) {
return qdrv_control_vlan_config((struct qdrv_mac *) (&qcb->macs[0]), argc, argv);
}
if (strcmp(name, "dyn-vlan") == 0) {
const char *addr;
uint16_t vid;
int ival;
if (argc != 4)
return -EINVAL;
addr = argv[2];
ival = simple_strtol(argv[3], NULL, 10);
vid = (qtn_vlan_is_valid(ival) ? (uint16_t)ival : QVLAN_DEF_PVID);
return qdrv_control_set_sta_vlan((struct qdrv_mac *) (&qcb->macs[0]),
addr, vid);
}
if (strcmp(name, "vlan-group") == 0) {
/* set vlan-group {ifname} {vlan_id} [0|1] */
const char *dev_name;
uint16_t vid;
int enable;
if (argc != 5) {
printk("vlan-group: invalid argument\n");
return -EINVAL;
}
dev_name = argv[2];
vid = simple_strtol(argv[3], NULL, 10);
enable = simple_strtol(argv[4], NULL, 10);
return qdrv_control_set_vlan_group((struct qdrv_mac *) (&qcb->macs[0]),
dev_name, vid, !!enable);
}
if (strcmp(name, "mac_reserve") == 0)
return qdrv_control_mac_reserve(argc - 2, &argv[2]);
if (strcmp(name, "wps_intf") == 0) {
if (argc != 3)
return -EINVAL;
const char *dev_name = argv[2];
struct net_device *ndev = dev_get_by_name(&init_net, dev_name);
if (!ndev)
return -EINVAL;
if (!(ndev->qtn_flags & QTN_FLAG_WIFI_DEVICE)) {
dev_put(ndev);
return -EINVAL;
}
qdrv_wps_button_exit();
qdrv_wps_button_init(ndev);
dev_put(ndev);
return 0;
}
if (strcmp(name, "br_isolate") == 0)
return qdrv_control_set_br_isolate(dev, argc - 2, &argv[2]);
for (i = 0; i < PARAM_TABLE_SIZE; i++) {
if (strcmp(name, s_param_table[i].name) == 0) {
break;
}
}
if (i == PARAM_TABLE_SIZE) {
DBGPRINTF_E("Parameter %s is not recognized\n", name);
return -1;
}
/* Check if a specific address is given */
if (s_param_table[i].address != NULL) {
dest = s_param_table[i].address;
} else if(strcmp(name, "uc_flags") == 0) {
/* setting something in shared parameters */
shared_params *sp = qtn_mproc_sync_shared_params_get();
if (sp == NULL) {
DBGPRINTF_E("shared_params struct not yet published\n");
return(-1);
}
dest = (char *) &sp->uc_flags;
} else if(strcmp(name, "vendor_fix") == 0) {
dest = (char *)&vendor_fix;
} else if(strcmp(name, "vap_default_state") == 0) {
dest = (char *)&vap_default_state;
} else if(strcmp(name, "brcm_rxglitch_thrshlds") == 0) {
dest = (char *)&brcm_rxglitch_thrshlds;
} else {
/* Use an offset into our control structure */
dest = (char *) qcb + s_param_table[i].offset;
}
if (s_param_table[i].flags & P_FL_TYPE_INT) {
if (value[0] == '0' && value[1] == 'x') {
if (sscanf(&value[2], "%x", (int *) dest) != 1)
goto error;
} else {
if (sscanf(value, "%d", (int *) dest) != 1)
goto error;
}
} else if (s_param_table[i].flags & P_FL_TYPE_STRING) {
strncpy(dest, value, s_param_table[i].size);
dest[s_param_table[i].size - 1] = '\0';
} else if(s_param_table[i].flags & P_FL_TYPE_MAC) {
if (qdrv_parse_mac(value, (uint8_t *) dest) < 0) {
goto error;
}
if ((uint8_t *)dest == &wifi_macaddr[0]) {
qw = (struct qdrv_wlan *)qcb->macs[0].data;
mac = (struct qdrv_mac *) (&qcb->macs[0]);
qdrv_hostlink_msg_set_wifi_macaddr(qw, &wifi_macaddr[0]);
memcpy(mac->mac_addr, wifi_macaddr, IEEE80211_ADDR_LEN);
memcpy(qcb->mac0, wifi_macaddr, IEEE80211_ADDR_LEN);
memcpy(qw->ic.ic_myaddr, wifi_macaddr, IEEE80211_ADDR_LEN);
}
}
/* Propagate any parameters into sub-structures of the qcb. */
qdrv_command_set_post(qcb);
if(strcmp(name, "test1") == 0) {
static int test_mode_pm_overide = 0;
if ((g_qdrv_radar_test_mode == 0x2) || (g_qdrv_radar_test_mode == 0x3)) {
if ((test_mode_pm_overide == 0) &&
(pm_qos_add_requirement(PM_QOS_POWER_SAVE, "war_test1",
BOARD_PM_LEVEL_FORCE_NO) == 0)) {
test_mode_pm_overide = 1;
}
g_dbg_log_module |= DBG_LM;
DBG_LOG_FUNC |= QDRV_LF_DFS_TESTMODE;
DBG_LOG_LEVEL = DBG_LL_NOTICE;
} else if (g_qdrv_radar_test_mode == 0x0) {
if (test_mode_pm_overide != 0) {
pm_qos_remove_requirement(PM_QOS_POWER_SAVE, "war_test1");
test_mode_pm_overide = 0;
}
DBG_LOG_FUNC &= ~QDRV_LF_DFS_TESTMODE;
DBG_LOG_LEVEL = DBG_LL_WARNING;
}
else
goto error;
} else if (strcmp(name, "vendor_fix") == 0) {
struct ieee80211com *ic;
int update_beacon = 0;
mac = qdrv_control_mac_get(dev, "0");
if (mac == NULL) {
DBGPRINTF_E("mac NULL\n");
goto error;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
goto error;
}
ic = &qw->ic;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX, "Previous vendor fix flag is 0x%x\n",
ic->ic_vendor_fix);
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"DHCP fix is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_DHCP) ? "enabled" : "disabled");
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"Replace IGMP src mac is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_REPLACE_IGMP_SRCMAC) ? "enabled" : "disabled");
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"Replace IP src mac is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_REPLACE_IP_SRCMAC) ? "enabled" : "disabled");
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"Drop STA IGMP query is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_DROP_STA_IGMPQUERY) ? "enabled" : "disabled");
if ((ic->ic_vendor_fix & VENDOR_FIX_BRCM_DHCP) != (vendor_fix & VENDOR_FIX_BRCM_DHCP)) {
update_beacon = 1;
}
ic->ic_vendor_fix = vendor_fix;
if (update_beacon) {
struct ieee80211vap *vap;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if (vap->iv_state != IEEE80211_S_RUN)
continue;
ic->ic_beacon_update(vap);
}
}
} else if (strcmp(name, "vap_default_state") == 0) {
struct ieee80211com *ic;
mac = qdrv_control_mac_get(dev, "0");
if (mac == NULL) {
DBGPRINTF_E("mac NULL\n");
goto error;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
goto error;
}
ic = &qw->ic;
ic->ic_vap_default_state = !!vap_default_state;
} else if (strcmp(name, "brcm_rxglitch_thrshlds") == 0) {
struct ieee80211com *ic;
struct brcm_rxglitch_thrshld_pair *pair;
int pwr, idx, rssi, pos;
uint32_t glitch;
ic = qdrv_get_ieee80211com(dev);
if (ic == NULL) {
return -1;
}
pair = ic->ic_scs.scs_brcm_rxglitch_thrshlds;
if ((brcm_rxglitch_thrshlds > 0) && (brcm_rxglitch_thrshlds <= BRCM_RXGLITCH_THRSHLD_SCALE_MAX)) {
ic->ic_scs.scs_brcm_rxglitch_thrshlds_scale = brcm_rxglitch_thrshlds;
} else {
printk("brcm rx glitch thresholds scale must be in range (%u, %u]\n", 0, BRCM_RXGLITCH_THRSHLD_SCALE_MAX);
}
printk("brcm rx glitch thresholds scale = %u\n", ic->ic_scs.scs_brcm_rxglitch_thrshlds_scale);
if (argc >= 7) {
pwr = STR2L(argv[3]);
idx = STR2L(argv[4]);
rssi = STR2L(argv[5]);
glitch = STR2L(argv[6]);
pos = pwr * BRCM_RXGLITH_THRSHLD_STEP + idx;
pair[pos].rssi = rssi;
pair[pos].rxglitch = glitch;
printk("Set pwr=%d, idx=%d to rssi=%d, glitch=%u\n", pwr, idx,
pair[pos].rssi, pair[pos].rxglitch);
} else {
printk("current brcm_rxglitch_thresholds:\n");
for (pwr = 0; pwr < BRCM_RXGLITH_THRSHLD_PWR_NUM; pwr++) {
for (idx = 0; idx < BRCM_RXGLITH_THRSHLD_STEP; idx++) {
pos = pwr * BRCM_RXGLITH_THRSHLD_STEP + idx;
printk("pwr=%d, idx=%d, rssi=%d, glitch=%u\n", pwr, idx,
pair[pos].rssi, pair[pos].rxglitch);
}
}
}
} else if(strcmp(name, "vlan_promisc") == 0) {
br_vlan_set_promisc(*((int*)dest));
} else if (strcmp(name, "pwr_mgmt") == 0) {
uint8_t tdls_peer_mac[IEEE80211_ADDR_LEN];
struct ieee80211com *ic;
if (qdrv_parse_mac(argv[3], (uint8_t *)tdls_peer_mac) < 0) {
goto error;
}
ic = qdrv_get_ieee80211com(dev);
if (ic == NULL)
return -1;
ieee80211_send_qosnulldata_ext(ic, tdls_peer_mac, *((int*)dest));
} else if (strcmp(name, "rxgain_params") == 0) {
struct ieee80211com *ic;
ic = qdrv_get_ieee80211com(dev);
if (argc >= 8) {
struct qtn_rf_rxgain_params rx_gain_params = {0};
int index = (int)STR2L(argv[2]);
rx_gain_params.lna_on_indx = (int8_t)STR2L(argv[3]);;
rx_gain_params.max_gain_idx = (int16_t)STR2L(argv[4]);
rx_gain_params.cs_thresh_dbm = (int16_t)STR2L(argv[5]);
rx_gain_params.cca_prim_dbm = (int16_t)STR2L(argv[6]);
rx_gain_params.cca_sec_scs_off_dbm = (int16_t)STR2L(argv[7]);
rx_gain_params.cca_sec_scs_on_dbm = (int16_t)STR2L(argv[8]);
qdrv_rxgain_params(ic, index, &rx_gain_params);
} else {
qdrv_rxgain_params(ic, 0, NULL);
}
}
return 0;
error:
DBGPRINTF_E("Value %s for parameter %s is invalid\n", value, name);
return -1;
}
static char *qdrv_show_bands(const int chipid)
{
switch (chipid) {
case CHIPID_2_4_GHZ:
return "2.4GHz";
break;
case CHIPID_5_GHZ:
return "5GHz";
break;
case CHIPID_DUAL:
return "dual";
break;
}
return "unknown";
}
#define QDRV_SHOW_PRINT(_s, _fmt, ...) do { \
if (s) \
seq_printf(_s, _fmt, ##__VA_ARGS__); \
else \
printk(_fmt, ##__VA_ARGS__); \
} while(0);
static char *qdrv_control_bld_type_str(enum qdrv_bld_type bld_type)
{
switch (bld_type) {
case QDRV_BLD_TYPE_ENG:
return "eng";
case QDRV_BLD_TYPE_BENCH:
return "bench";
case QDRV_BLD_TYPE_BUILDBOT:
return "buildbot";
case QDRV_BLD_TYPE_REL:
return "release";
case QDRV_BLD_TYPE_SDK:
return "SDK";
case QDRV_BLD_TYPE_GPL:
return "GPL";
}
return "unknown";
}
static void qdrv_show_info(struct seq_file *s, void *data, uint32_t num)
{
struct qdrv_mac *mac = (struct qdrv_mac *)data;
struct qdrv_cb *qcb = container_of(mac, struct qdrv_cb, macs[mac->unit]);
struct ieee80211com *ic = NULL;
struct qdrv_wlan *qw;
#define QDRV_SWVER_STR_MAX 20
char swver[QDRV_SWVER_STR_MAX] = { 0 };
qw = qdrv_control_wlan_get(mac);
if (qw) {
ic = &qw->ic;
snprintf(swver, sizeof(swver) - 1, DBGFMT_BYTEFLD4_P,
DBGFMT_BYTEFLD4_V(ic->ic_ver_sw));
}
QDRV_SHOW_PRINT(s, "Build name: %s\n", QDRV_BLD_NAME);
QDRV_SHOW_PRINT(s, "Build revision: %s\n", QDRV_BLD_REV);
QDRV_SHOW_PRINT(s, "Build type: %s\n", qdrv_control_bld_type_str(QDRV_BLD_TYPE));
QDRV_SHOW_PRINT(s, "Build timestamp: %lu\n", QDRV_BUILDDATE);
if (strcmp(QDRV_BLD_NAME, QDRV_BLD_LABEL) != 0)
QDRV_SHOW_PRINT(s, "Software label: %s\n", QDRV_BLD_LABEL);
QDRV_SHOW_PRINT(s, "Platform ID: %u\n", QDRV_CFG_PLATFORM_ID);
QDRV_SHOW_PRINT(s, "Hardware ID: %s\n", qdrv_soc_get_hw_id(0));
if (ic) {
QDRV_SHOW_PRINT(s, "Hardware revision: %s\n", qdrv_soc_get_hw_rev_desc(ic->ic_ver_hw));
QDRV_SHOW_PRINT(s, "Band: %s\n", qdrv_show_bands(ic ? ic->ic_rf_chipid : -1));
}
QDRV_SHOW_PRINT(s, "Kernel version: " DBGFMT_BYTEFLD3_P "\n",
DBGFMT_BYTEFLD3_V(LINUX_VERSION_CODE));
QDRV_SHOW_PRINT(s, "Calibration version: %s\n", qcb->algo_version);
QDRV_SHOW_PRINT(s, "DC/IQ cal version: %s\n", dc_iq_calfile_version);
QDRV_SHOW_PRINT(s, "Power cal version: %s\n", power_calfile_version);
QDRV_SHOW_PRINT(s, "MuC firmware: %s\n", qcb->muc_firmware);
QDRV_SHOW_PRINT(s, "DSP firmware: %s\n", qcb->dsp_firmware);
QDRV_SHOW_PRINT(s, "AuC firmware: %s\n", qcb->auc_firmware);
QDRV_SHOW_PRINT(s, "MAC address 0: %pM\n", qcb->mac0);
QDRV_SHOW_PRINT(s, "MAC address 1: %pM\n", qcb->mac1);
QDRV_SHOW_PRINT(s, "U-Boot version: %s\n", RUBY_UBOOT_VERSION);
}
static void
qdrv_show_hw_desc( struct seq_file *s, void *data, u32 num )
{
seq_printf(s, "%s\n", qdrv_soc_get_hw_desc(0));
}
static void
qdrv_show_mucfw( struct seq_file *s, void *data, u32 num )
{
struct qdrv_cb *qcb = (struct qdrv_cb *) data;
seq_printf( s, "%s\n", qcb->muc_firmware );
}
static int
qdrv_fw_is_internal(enum qdrv_bld_type bld_type)
{
switch(bld_type) {
case QDRV_BLD_TYPE_REL:
case QDRV_BLD_TYPE_SDK:
case QDRV_BLD_TYPE_GPL:
return 0;
case QDRV_BLD_TYPE_ENG:
case QDRV_BLD_TYPE_BENCH:
case QDRV_BLD_TYPE_BUILDBOT:
return 1;
}
return 1;
}
static void
qdrv_show_fw_ver(struct seq_file *s, void *data, u32 num)
{
struct ieee80211com *ic = data;
if (qdrv_fw_is_internal(QDRV_BLD_TYPE)) {
seq_printf(s, "%s\n", QDRV_BLD_NAME);
} else {
seq_printf(s, DBGFMT_BYTEFLD4_P "\n", DBGFMT_BYTEFLD4_V(ic->ic_ver_sw));
}
}
static void
qdrv_show_platform_id( struct seq_file *s, void *data, u32 num )
{
seq_printf( s, "%u\n", QDRV_CFG_PLATFORM_ID );
}
static void
qdrv_show_checksum( struct seq_file *s, void *data, u32 num )
{
struct qdrv_cb *qcb = (struct qdrv_cb *) data;
if (qcb->power_table_ctrl.reading_checksum) {
seq_printf( s, "%s\n", qcb->power_table_ctrl.reading_checksum->checksum);
} else {
seq_printf( s, "NA\n");
}
qcb->power_table_ctrl.reading_checksum = NULL;
}
static void
qdrv_show_muc_value( struct seq_file *s, void *data, u32 num )
{
struct qdrv_cb *qcb = (struct qdrv_cb *) data;
seq_printf( s, "%d\n", (int) qcb->value_from_muc );
}
static void
qdrv_show_auc_stats(struct seq_file *s, void *data, u32 num)
{
const struct shared_params *sp = qtn_mproc_sync_shared_params_get();
const bool fw_no_mu = sp->fw_no_mu;
const struct qtn_auc_stat_field *auc_field_stats = fw_no_mu ? auc_field_stats_nomu :
auc_field_stats_default;
const size_t nstats = fw_no_mu ? ARRAY_SIZE(auc_field_stats_nomu) :
ARRAY_SIZE(auc_field_stats_default);
unsigned int i;
for (i = 0; i < nstats; i++) {
const uintptr_t addr = auc_field_stats[i].addr;
const char *const name = auc_field_stats[i].name;
uint32_t val = *((const uint32_t *) addr);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, name, val);
}
}
/* Calculate avarage value for histogram */
static uint32_t qdrv_get_hist_avr(uint32_t *histogram, uint32_t size, uint32_t width)
{
uint32_t i;
uint32_t sum1, sum2;
for (i = 0, sum1 = 0, sum2 = 0; i < size; i++) {
sum1 += (2*width*i + (width - 1))*histogram[i];
sum2 += histogram[i];
}
return sum1/sum2/2;
}
static void
qdrv_show_dsp_time_histogram(struct seq_file *s,
volatile struct qtn_txbf_mbox *txbf_mbox)
{
int i;
/* ------------------------------------------------------------------------ */
seq_printf(s, "%-*s ", QDRV_UC_STATS_DESC_LEN, "dsp_mu_qmat_qmem_copy_time_hist");
for (i = 0; i < FIELD_ARRAY_SIZE(struct qtn_dsp_stats, dsp_mu_qmat_qmem_copy_time_hist); i++) {
seq_printf(s, "%-2u-%-2uus ", i*DSP_MU_QMAT_COPY_TIME_HIST_WIDTH_US,
(i + 1)*DSP_MU_QMAT_COPY_TIME_HIST_WIDTH_US - 1);
}
seq_printf(s, "avr us");
seq_printf(s, "\n%-*s ", QDRV_UC_STATS_DESC_LEN, "dsp_mu_qmat_qmem_copy_time_hist");
for (i = 0; i < FIELD_ARRAY_SIZE(struct qtn_dsp_stats, dsp_mu_qmat_qmem_copy_time_hist); i++) {
seq_printf(s, "%-7u ", txbf_mbox->dsp_stats.dsp_mu_qmat_qmem_copy_time_hist[i]);
}
seq_printf(s, "%-7u\n", qdrv_get_hist_avr((uint32_t*)&txbf_mbox->dsp_stats.dsp_mu_qmat_qmem_copy_time_hist[0],
FIELD_ARRAY_SIZE(struct qtn_dsp_stats, dsp_mu_qmat_qmem_copy_time_hist),
DSP_MU_QMAT_COPY_TIME_HIST_WIDTH_US));
seq_printf(s, "%-*s", QDRV_UC_STATS_DESC_LEN, "dsp_mu_qmat_qmem_copy_time_hist");
seq_printf(s, "total maximum is %u us\n", txbf_mbox->dsp_stats.dsp_mu_qmat_qmem_copy_time_max);
/* ------------------------------------------------------------------------ */
seq_printf(s, "%-*s ", QDRV_UC_STATS_DESC_LEN, "dsp_mu_qmat_inst_time_hist");
for (i = 0; i < FIELD_ARRAY_SIZE(struct qtn_dsp_stats, dsp_mu_qmat_inst_time_hist); i++) {
seq_printf(s, "%-2u-%-2ums ", i*DSP_MU_QMAT_INST_TIME_HIST_WIDTH_MS,
(i + 1)*DSP_MU_QMAT_INST_TIME_HIST_WIDTH_MS - 1);
}
seq_printf(s, "avr ms");
seq_printf(s, "\n%-*s ", QDRV_UC_STATS_DESC_LEN, "dsp_mu_qmat_inst_time_hist");
for (i = 0; i < FIELD_ARRAY_SIZE(struct qtn_dsp_stats, dsp_mu_qmat_inst_time_hist); i++) {
seq_printf(s, "%-7u ", txbf_mbox->dsp_stats.dsp_mu_qmat_inst_time_hist[i]);
}
seq_printf(s, "%-7u\n", qdrv_get_hist_avr((uint32_t*)&txbf_mbox->dsp_stats.dsp_mu_qmat_inst_time_hist[0],
FIELD_ARRAY_SIZE(struct qtn_dsp_stats, dsp_mu_qmat_inst_time_hist),
DSP_MU_QMAT_INST_TIME_HIST_WIDTH_MS));
seq_printf(s, "%-*s ", QDRV_UC_STATS_DESC_LEN, "dsp_mu_qmat_inst_time_hist");
seq_printf(s, "total maximum is %u ms\n", txbf_mbox->dsp_stats.dsp_mu_qmat_inst_time_max);
/* ------------------------------------------------------------------------ */
}
static void
qdrv_show_dsp_stats(struct seq_file *s, void *data, u32 num)
{
#if DSP_ENABLE_STATS
int i;
volatile struct qtn_txbf_mbox *txbf_mbox = qtn_txbf_mbox_get();
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ndp_rx", txbf_mbox->dsp_stats.dsp_ndp_rx);
seq_printf(s, "%-*s %-3s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n",
QDRV_UC_STATS_DESC_LEN,
"dsp_act_rx", "aid", "total", "mu_gr_sl", "mu_prec", "su", "bad",
"mu_drop", "mu_nexp", "mu_lock", "mu_rl_nu", "inv_len");
for (i = 0; i < ARRAY_SIZE(txbf_mbox->dsp_stats.dsp_act_rx); i++)
seq_printf(s, "%-*s %-3u %-8u %-8u %-8u %-8u %-8u %-8u %-8u %-8u %-8u %-8u\n",
QDRV_UC_STATS_DESC_LEN, "dsp_act_rx", i,
txbf_mbox->dsp_stats.dsp_act_rx[i],
txbf_mbox->dsp_stats.dsp_act_rx_mu_grp_sel[i],
txbf_mbox->dsp_stats.dsp_act_rx_mu_prec[i],
txbf_mbox->dsp_stats.dsp_act_rx_su[i],
txbf_mbox->dsp_stats.dsp_act_rx_bad[i],
txbf_mbox->dsp_stats.dsp_act_rx_mu_drop[i],
txbf_mbox->dsp_stats.dsp_act_rx_mu_nexp[i],
txbf_mbox->dsp_stats.dsp_act_rx_mu_lock_cache[i],
txbf_mbox->dsp_stats.dsp_act_rx_mu_rel_nuse[i],
txbf_mbox->dsp_stats.dsp_act_rx_inval_len[i]);
seq_printf(s, "%-*s %-3s %-4s %-4s %-8s %-8s %-8s %-8s\n", QDRV_UC_STATS_DESC_LEN,
"dsp_mu_grp", "grp", "aid0", "aid1", "rank", "inst_ok", "upd_ok",
"upd_fl");
for (i = 0; i < ARRAY_SIZE(txbf_mbox->dsp_stats.dsp_mu_grp_inst_success); i++) {
seq_printf(s, "%-*s %-3u %-4u %-4u %-8d %-8u %-8u %-8u\n",
QDRV_UC_STATS_DESC_LEN, "dsp_mu_grp", i + 1,
txbf_mbox->dsp_stats.dsp_mu_grp_aid0[i],
txbf_mbox->dsp_stats.dsp_mu_grp_aid1[i],
txbf_mbox->dsp_stats.dsp_mu_grp_rank[i],
txbf_mbox->dsp_stats.dsp_mu_grp_inst_success[i],
txbf_mbox->dsp_stats.dsp_mu_grp_update_success[i],
txbf_mbox->dsp_stats.dsp_mu_grp_update_fail[i]);
}
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_del_mu_node_rx", txbf_mbox->dsp_stats.dsp_del_mu_node_rx);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_act_tx", txbf_mbox->dsp_stats.dsp_act_tx);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_act_free_tx", txbf_mbox->dsp_stats.dsp_act_free_tx);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ndp_discarded", txbf_mbox->dsp_stats.dsp_ndp_discarded);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ndp_inv_bw", txbf_mbox->dsp_stats.dsp_ndp_inv_bw);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ndp_inv_len", txbf_mbox->dsp_stats.dsp_ndp_inv_len);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ndp_max_len", txbf_mbox->dsp_stats.dsp_ndp_max_len);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_inst_mu_grp_tx", txbf_mbox->dsp_stats.dsp_inst_mu_grp_tx);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "wr", txbf_mbox->wr);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "muc_to_dsp_action_frame_mbox[0]", txbf_mbox->muc_to_dsp_action_frame_mbox[0]);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "muc_to_dsp_action_frame_mbox[1]", txbf_mbox->muc_to_dsp_action_frame_mbox[1]);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "muc_to_dsp_ndp_mbox", txbf_mbox->muc_to_dsp_ndp_mbox);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "muc_to_dsp_del_grp_node_mbox", txbf_mbox->muc_to_dsp_del_grp_node_mbox);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_to_host_mbox", txbf_mbox->dsp_to_host_mbox);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ipc_in", txbf_mbox->dsp_stats.dsp_ipc_in);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ipc_out", txbf_mbox->dsp_stats.dsp_ipc_out);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_exc", txbf_mbox->dsp_stats.dsp_exc);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_ipc_int", txbf_mbox->dsp_stats.dsp_ipc_int);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_timer_int", txbf_mbox->dsp_stats.dsp_timer_int);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_timer1_int", txbf_mbox->dsp_stats.dsp_timer1_int);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_last_int", txbf_mbox->dsp_stats.dsp_last_int);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_flag", txbf_mbox->dsp_stats.dsp_flag);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_point", txbf_mbox->dsp_stats.dsp_point);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_sleep_in", txbf_mbox->dsp_stats.dsp_sleep_in);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_sleep_out", txbf_mbox->dsp_stats.dsp_sleep_out);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_qmat_invalid", txbf_mbox->dsp_stats.dsp_qmat_invalid);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_sram_qmat_num", txbf_mbox->dsp_stats.dsp_sram_qmat_num);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_err_neg_qmat_num", txbf_mbox->dsp_stats.dsp_err_neg_qmat_num);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_stat_bad_stack", txbf_mbox->dsp_stats.dsp_stat_bad_stack);
uint32_t reg = qtn_mproc_sync_mem_read(RUBY_SYS_CTL_M2D_INT);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "RUBY_SYS_CTL_M2D_INT", reg);
reg = qtn_mproc_sync_mem_read(RUBY_SYS_CTL_D2L_INT);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "RUBY_SYS_CTL_D2L_INT", reg);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_status32", txbf_mbox->dsp_stats.dsp_status32);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_status32_l1", txbf_mbox->dsp_stats.dsp_status32_l1);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_status32_l2", txbf_mbox->dsp_stats.dsp_status32_l2);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_ilink1", txbf_mbox->dsp_stats.dsp_ilink1);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_ilink2", txbf_mbox->dsp_stats.dsp_ilink2);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_blink", txbf_mbox->dsp_stats.dsp_blink);
seq_printf(s, "%-*s 0x%08x\n", QDRV_UC_STATS_DESC_LEN, "dsp_sp", txbf_mbox->dsp_stats.dsp_sp);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_time", txbf_mbox->dsp_stats.dsp_time);
seq_printf(s, "%-*s %d, %d, %d, %d\n", QDRV_UC_STATS_DESC_LEN, "mu_D_user1",
txbf_mbox->dsp_stats.dspmu_D_user1[0], txbf_mbox->dsp_stats.dspmu_D_user1[1],
txbf_mbox->dsp_stats.dspmu_D_user1[2], txbf_mbox->dsp_stats.dspmu_D_user1[3]);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "mu_intf_user1", txbf_mbox->dsp_stats.dspmu_max_intf_user1);
seq_printf(s, "%-*s %d, %d, %d, %d\n", QDRV_UC_STATS_DESC_LEN, "mu_D_user2",
txbf_mbox->dsp_stats.dspmu_D_user2[0], txbf_mbox->dsp_stats.dspmu_D_user2[1],
txbf_mbox->dsp_stats.dspmu_D_user2[2], txbf_mbox->dsp_stats.dspmu_D_user2[3]);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "mu_intf_user2", txbf_mbox->dsp_stats.dspmu_max_intf_user2);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "mu rank criteria", txbf_mbox->rank_criteria_to_use);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_trig_mu_grp_sel", txbf_mbox->dsp_stats.dsp_trig_mu_grp_sel);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_rank_success", txbf_mbox->dsp_stats.dsp_mu_rank_success);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_rank_fail", txbf_mbox->dsp_stats.dsp_mu_rank_fail);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_grp_inv_act", txbf_mbox->dsp_stats.dsp_mu_grp_inv_act);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_act_cache_expired[grp_sel]", txbf_mbox->dsp_stats.dsp_act_cache_expired[0]);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_act_cache_expired[prec]", txbf_mbox->dsp_stats.dsp_act_cache_expired[1]);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_grp_upd_done", txbf_mbox->dsp_stats.dsp_mu_grp_upd_done);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_node_del", txbf_mbox->dsp_stats.dsp_mu_node_del);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_grp_inst_fail", txbf_mbox->dsp_stats.dsp_mu_grp_inst_fail);
seq_printf(s, "%-*s %d\n", QDRV_UC_STATS_DESC_LEN, "dsp_mimo_ctrl_fail", txbf_mbox->dsp_stats.dsp_mimo_ctrl_fail);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_fb_80mhz", txbf_mbox->dsp_stats.dsp_mu_fb_80mhz);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_fb_40mhz", txbf_mbox->dsp_stats.dsp_mu_fb_40mhz);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_fb_20mhz", txbf_mbox->dsp_stats.dsp_mu_fb_20mhz);
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "dsp_mu_drop_20mhz", txbf_mbox->dsp_stats.dsp_mu_drop_20mhz);
seq_printf(s, "%-*s %-3s %-3s %-3s %-3s %-8s\n", QDRV_UC_STATS_DESC_LEN,
"txbf_msg_bufs", "idx", "st", "mt", "aid", "cnt");
for (i = 0; i < ARRAY_SIZE(txbf_mbox->txbf_msg_bufs); i++) {
seq_printf(s, "%-*s %-3u %-3u %-3u %-3u %-8u\n",
QDRV_UC_STATS_DESC_LEN, "txbf_msg_bufs", i,
txbf_mbox->txbf_msg_bufs[i].state,
txbf_mbox->txbf_msg_bufs[i].msg_type,
txbf_mbox->txbf_msg_bufs[i].aid,
txbf_mbox->txbf_msg_bufs[i].counter);
}
qdrv_show_dsp_time_histogram(s, txbf_mbox);
#else
seq_printf(s, "%-*s\n", QDRV_UC_STATS_DESC_LEN, "DSP_ENABLE_STATS must be defined to enable DSP stats");
#endif
}
static void
qdrv_show_uc_tx_stats(struct seq_file *s, void *data, u32 num)
{
struct qdrv_mac *mac = (struct qdrv_mac *) data;
struct qtn_stats_log *iw_stats_log;
struct qdrv_wlan *qw;
const u32* tx_stats;
int i;
const char *tx_stats_names[] = MUC_TX_STATS_NAMES_TABLE;
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return;
}
iw_stats_log = qw->mac->mac_sys_stats;
if (iw_stats_log == NULL) {
return;
}
qdrv_pktlogger_flush_data(qw);
if (qw->pktlogger.stats_uc_tx_ptr == NULL) {
qw->pktlogger.stats_uc_tx_ptr = ioremap_nocache(muc_to_lhost((u32)iw_stats_log->tx_muc_stats),
sizeof(struct muc_tx_stats));
if (qw->pktlogger.stats_uc_tx_ptr == NULL)
return;
}
tx_stats = qw->pktlogger.stats_uc_tx_ptr;
for (i = 0; i < ARRAY_SIZE(tx_stats_names); i++) {
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, tx_stats_names[i], tx_stats[i]);
}
}
static void
qdrv_show_uc_rx_stats(struct seq_file *s, void *data, u32 num)
{
struct qdrv_mac *mac = (struct qdrv_mac *) data;
struct qtn_stats_log *iw_stats_log;
struct qdrv_wlan *qw;
struct muc_rx_rates *uc_rx_rates;
struct muc_rx_bf_stats *uc_rx_bf_stats;
const u32* rx_stats;
int i;
const char *rx_stats_names[] = MUC_RX_STATS_NAMES_TABLE;
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return;
}
iw_stats_log = qw->mac->mac_sys_stats;
if (iw_stats_log == NULL) {
return;
}
qdrv_pktlogger_flush_data(qw);
if (qw->pktlogger.stats_uc_rx_ptr == NULL) {
qw->pktlogger.stats_uc_rx_ptr = ioremap_nocache(muc_to_lhost((u32)iw_stats_log->rx_muc_stats),
sizeof(struct muc_rx_stats));
if (qw->pktlogger.stats_uc_rx_ptr == NULL)
return;
}
if (qw->pktlogger.stats_uc_rx_rate_ptr == NULL) {
qw->pktlogger.stats_uc_rx_rate_ptr = ioremap_nocache(muc_to_lhost((u32)iw_stats_log->rx_muc_rates),
sizeof(struct muc_rx_rates));
if (qw->pktlogger.stats_uc_rx_rate_ptr == NULL)
return;
}
if (qw->pktlogger.stats_uc_rx_bf_ptr == NULL) {
qw->pktlogger.stats_uc_rx_bf_ptr = ioremap_nocache(muc_to_lhost((u32)iw_stats_log->rx_muc_bf_stats),
sizeof(struct muc_rx_bf_stats));
if (qw->pktlogger.stats_uc_rx_bf_ptr == NULL)
return;
}
/* Stats */
rx_stats = qw->pktlogger.stats_uc_rx_ptr;
uc_rx_rates = (struct muc_rx_rates *)qw->pktlogger.stats_uc_rx_rate_ptr;
for (i = 0; i < ARRAY_SIZE(rx_stats_names); i++) {
seq_printf(s, "%-*s %u\n", QDRV_UC_STATS_DESC_LEN, rx_stats_names[i], rx_stats[i]);
}
/* Beamforming */
uc_rx_bf_stats = (struct muc_rx_bf_stats *)qw->pktlogger.stats_uc_rx_bf_ptr;
for (i = 0; i < QTN_STATS_NUM_BF_SLOTS; i++) {
seq_printf(s, "\nBF slot=%u aid=%u ng=%u rx:", i, uc_rx_bf_stats->rx_bf_aid[i],
uc_rx_bf_stats->rx_bf_ng[i]);
if (uc_rx_bf_stats->rx_bf_valid[i] == 0) {
seq_printf(s, " free");
} else if ((i > 0) && (uc_rx_bf_stats->rx_bf_aid[i - 1] == uc_rx_bf_stats->rx_bf_aid[i])) {
seq_printf(s, " dual slot");
}
if (uc_rx_bf_stats->rx_bf_11ac_ndp[i] || uc_rx_bf_stats->rx_bf_11ac_act[i] || uc_rx_bf_stats->rx_bf_11ac_grp_sel[i]) {
seq_printf(s, " (11ac ndp=%u act=%u grp sel=%u prec=%u "
"su=%u bad=%u fail=%u gradd=%u grdel=%u)",
uc_rx_bf_stats->rx_bf_11ac_ndp[i],
uc_rx_bf_stats->rx_bf_11ac_act[i],
uc_rx_bf_stats->rx_bf_11ac_grp_sel[i],
uc_rx_bf_stats->rx_bf_11ac_prec[i],
uc_rx_bf_stats->rx_bf_11ac_su[i],
uc_rx_bf_stats->rx_bf_11ac_bad[i],
uc_rx_bf_stats->rx_bf_11ac_dsp_fail[i],
uc_rx_bf_stats->mu_grp_add[i],
uc_rx_bf_stats->mu_grp_del[i]);
}
if (uc_rx_bf_stats->rx_bf_11n_ndp[i] || uc_rx_bf_stats->rx_bf_11n_act[i]) {
seq_printf(s, " (11n ndp=%u act=%u)",
uc_rx_bf_stats->rx_bf_11n_ndp[i], uc_rx_bf_stats->rx_bf_11n_act[i]);
}
}
seq_printf(s, "\n%-*s %u\n", QDRV_UC_STATS_DESC_LEN, "BF msg_buf_alloc_fail",
uc_rx_bf_stats->msg_buf_alloc_fail);
/* 11n rates */
for (i = 0; i < ARRAY_SIZE(uc_rx_rates->rx_mcs); i++) {
if ((i % 10) == 0) {
seq_printf(s, "\nmcs_11n ");
}
seq_printf(s, " %2d:%-9u", i, uc_rx_rates->rx_mcs[i]);
}
/* 11ac rates */
for (i = 0; i < ARRAY_SIZE(uc_rx_rates->rx_mcs_11ac); i++) {
if ((i % 10) == 0) {
seq_printf(s, "\nmcs_11ac");
}
seq_printf(s, " %2d:%-9u", i, uc_rx_rates->rx_mcs_11ac[i]);
}
seq_printf(s, "\n");
}
static void
qdrv_show_debug_level( char *type )
{
printk("%s dump enabled, dump length %d", type, g_dbg_dump_pkt_len);
if (DBG_LOG_FUNC_TEST(QDRV_LF_DUMP_BEACON)) {
printk(", dump beacon frame");
}
if (DBG_LOG_FUNC_TEST(QDRV_LF_DUMP_ACTION)) {
printk(", dump action frame");
}
if (DBG_LOG_FUNC_TEST(QDRV_LF_DUMP_MGT)) {
printk(", dump management frame (exclude action frame)");
}
if (DBG_LOG_FUNC_TEST(QDRV_LF_DUMP_DATA)) {
printk(", dump data frame");
}
printk(".\n");
}
static void
qdrv_show_vendor_fix( struct seq_file *s, void *data, u32 num )
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) data;
struct ieee80211com *ic = &qw->ic;
seq_printf(s, "0x%x\n", ic->ic_vendor_fix);
}
static void
qdrv_show_vap_default_state( struct seq_file *s, void *data, u32 num )
{
struct qdrv_wlan *qw = (struct qdrv_wlan *) data;
struct ieee80211com *ic = &qw->ic;
seq_printf(s, "%d\n", ic->ic_vap_default_state);
}
static void
qdrv_control_show_vlan_dev(struct seq_file *s, struct qtn_vlan_dev *vdev)
{
struct qtn_vlan_config *vcfg;
int ret;
vcfg = kzalloc(sizeof(struct qtn_vlan_config), GFP_KERNEL);
if (!vcfg) {
printk(KERN_ERR"Not enough memory to print QVLAN configuration\n");
return;
}
if (vlan_enabled) {
vcfg->vlan_cfg = vdev->pvid & QVLAN_MASK_VID;
vcfg->vlan_cfg |= ((struct qtn_vlan_user_interface *)(vdev->user_data))->mode << QVLAN_SHIFT_MODE;
vcfg->priority = vdev->priority;
memcpy(vcfg->u.dev_config.member_bitmap, vdev->u.member_bitmap, sizeof(vcfg->u.dev_config.member_bitmap));
memcpy(vcfg->u.dev_config.tag_bitmap, vdev->tag_bitmap, sizeof(vcfg->u.dev_config.tag_bitmap));
} else {
memset(vcfg, 0, sizeof(*vcfg));
vcfg->vlan_cfg = (QVLAN_MODE_DISABLED << QVLAN_SHIFT_MODE);
}
ret = seq_write(s, vcfg, sizeof(struct qtn_vlan_config));
if (ret)
printk(KERN_ERR"VLAN info could not be written to seq file\n");
kfree(vcfg);
}
static void
qdrv_control_show_vlan_config(struct seq_file *s, void *data, u32 num)
{
int dev;
int ret;
struct qtn_vlan_dev *vdev;
struct qtn_vlan_config *vcfg;
struct net_device *ndev;
if (!strcmp(data, "tagrx")) {
vcfg = kzalloc(sizeof(struct qtn_vlan_config), GFP_KERNEL);
if (!vcfg) {
printk(KERN_ERR"Not enough memory to print QVLAN configuration\n");
return;
}
if (vlan_enabled) {
vcfg->vlan_cfg = 0;
memcpy(vcfg->u.tagrx_config, qtn_vlan_info.vlan_tagrx_bitmap, sizeof(vcfg->u.tagrx_config));
} else {
memset(vcfg, 0, sizeof(*vcfg));
vcfg->vlan_cfg = (QVLAN_MODE_DISABLED << QVLAN_SHIFT_MODE);
}
ret = seq_write(s, vcfg, sizeof(struct qtn_vlan_config));
if (ret)
printk(KERN_ERR"VLAN info could not be written to seq file\n");
kfree(vcfg);
} else {
for (dev = 0; dev < VLAN_INTERFACE_MAX; dev++) {
vdev = vdev_tbl_lhost[dev];
if (vdev) {
ndev = dev_get_by_index(&init_net, vdev->ifindex);
if (unlikely(!ndev))
break;
if (!strncmp(ndev->name, data, IFNAMSIZ)) {
dev_put(ndev);
qdrv_control_show_vlan_dev(s, vdev);
break;
} else {
dev_put(ndev);
}
}
}
}
}
static void qdrv_control_show_assoc(struct qdrv_mac *mac, int argc, char *argv[])
{
struct qdrv_show_assoc_params params;
qdrv_show_assoc_init_params(&params, mac);
if (qdrv_show_assoc_parse_params(&params, argc, argv) != 0) {
qdrv_control_set_show(qdrv_show_assoc_print_usage, &g_show_assoc_params, 1, 1);
return;
}
if (down_interruptible(&s_output_sem)) {
return;
}
g_show_assoc_params = params;
up(&s_output_sem);
qdrv_control_set_show(qdrv_show_assoc_print_stats, &g_show_assoc_params, 1, 1);
}
static void qdrv_show_core_dump_size( struct seq_file *s, void *data, uint32_t num )
{
seq_printf(s, "%u\n", *((uint32_t *) data));
}
static void qdrv_show_core_dump( struct seq_file *s, void *data, uint32_t num )
{
char byte;
uint32_t i;
for (i = 0; i < num; ++i) {
/* Only for debug - should be "off" in production code */
#if 0
byte = (char) ((i % 26) + 'A');
#else
byte = *(((char *) data) + i);
#endif
seq_putc(s, byte);
}
}
static void qdrv_show_wps_intf(struct seq_file *s, void *data, uint32_t num)
{
struct net_device *ndev = qdrv_wps_button_get_dev();
seq_printf(s, "%s", ndev->name);
}
static void
qdrv_control_get_br_isolate(struct seq_file *s, void *data, u32 num)
{
struct qdrv_mac *mac = (struct qdrv_mac *)data;
struct qdrv_wlan *qw;
uint32_t val;
qw = qdrv_control_wlan_get(mac);
if (unlikely(!qw))
return;
val = ((qw->br_isolate_vid << 16) | qw->br_isolate);
seq_write(s, &val, sizeof(val));
}
static int qdrv_command_get(struct device *dev, int argc, char *argv[])
{
struct qdrv_cb *qcb;
struct qdrv_mac *mac;
struct qdrv_wlan *qw;
struct ieee80211com *ic = NULL;
if (!dev) {
return -1;
}
qcb = dev_get_drvdata(dev);
if (!qcb) {
return -1;
}
if (argc < 3) {
DBGPRINTF_E("Invalid number of arguments\n");
return -1;
}
mac = qdrv_control_mac_get(dev, argv[1]);
if (!mac) {
DBGPRINTF_E("mac not found\n");
return -1;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return -1;
}
ic = &qw->ic;
if(strcmp(argv[2], "stats") == 0) {
qdrv_soc_stats(qcb, mac);
} else if(strcmp(argv[2], "info") == 0) {
qdrv_control_set_show(qdrv_show_info, mac, 1, 1);
} else if(strcmp(argv[2], "info_log") == 0) {
qdrv_show_info(NULL, mac, 1);
} else if(strcmp(argv[2], "muc_stats") == 0) {
qdrv_muc_stats_printlog(qcb, mac, &(qw->ic), argc - 3, &argv[3]);
} else if(strcmp(argv[2], "assoc_info") == 0) {
qdrv_wlan_get_assoc_info(qw);
} else if(strcmp(argv[2], "show_assoc") == 0) {
qdrv_control_show_assoc(mac, argc - 3, &argv[3]);
} else if(strcmp(argv[2], "assoc_q") == 0) {
qdrv_wlan_get_assoc_queue_info(qw);
} else if(strcmp(argv[2], "chip_id") == 0) {
u32 local_chip_id = chip_id();
printk("Chip ID: %u (0x%x)\n", local_chip_id, local_chip_id );
} else if (strcmp(argv[2], "mucfw") == 0) {
qdrv_control_set_show(qdrv_show_mucfw, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "fwver") == 0) {
qdrv_control_set_show(qdrv_show_fw_ver, ic, 1, 1);
} else if (strcmp(argv[2], "platform_id") == 0) {
qdrv_control_set_show(qdrv_show_platform_id, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "noise") == 0) {
qcb->value_from_muc = qdrv_muc_get_noise(mac, ic);
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "rssi") == 0) {
unsigned int rf_chain = 0;
if (argc > 3) {
if(sscanf(argv[3], "%u", &rf_chain) != 1) {
rf_chain = 0;
}
}
qcb->value_from_muc = qdrv_muc_get_rssi_by_chain(mac, ic, rf_chain);
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "rx_gain") == 0) {
qcb->value_from_muc = qdrv_muc_get_rx_gain_fields(mac, ic);
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "max_gain") == 0) {
#if TOPAZ_FPGA_PLATFORM
qcb->value_from_muc = (0 && qdrv_read_mem(RUBY_QT3_BB_TD_MAX_GAIN));
#else
qcb->value_from_muc = qdrv_read_mem(RUBY_QT3_BB_TD_MAX_GAIN);
#endif
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "ext_lna_gain") == 0) {
shared_params *sp = qtn_mproc_sync_shared_params_get();
if (sp == NULL) {
DBGPRINTF_E("shared_params struct not yet published\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
if (sp->ext_lna_gain < QTN_EXT_LNA_GAIN_MAX) {
printk("ext_lna_gain : %d.", sp->ext_lna_gain);
} else {
printk("ext_lna_gain(%d) is invalid.", sp->ext_lna_gain);
}
if (sp->ext_lna_bypass_gain < QTN_EXT_LNA_GAIN_MAX) {
printk(" ext_lna_bypass_gain : %d.\n", sp->ext_lna_bypass_gain);
} else {
printk(" ext_lna_bypass_gain(%d) is invalid.\n", sp->ext_lna_bypass_gain);
}
} else if (strcmp(argv[2], "node_info") == 0) {
ieee80211_dump_nodes(&ic->ic_sta);
} else if (strcmp(argv[2], "phy_stat") == 0) {
unsigned int stat_index = 0;
int stat_value;
if (argc < 4) {
return -1;
}
if (argc > 4) {
if (sscanf(argv[4], "%u", &stat_index) != 1) {
return -1;
}
}
if (qdrv_muc_get_phy_stat(mac, ic, argv[3], stat_index, &stat_value)) {
return -1;
}
qcb->value_from_muc = stat_value;
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "debug_flag") == 0) {
if (DBG_LOG_FUNC_TEST(QDRV_LF_DUMP_RX_PKT)) {
qdrv_show_debug_level("RX pkt");
} else {
printk("RX pkt dump disabled.\n");
}
if (DBG_LOG_FUNC_TEST(QDRV_LF_DUMP_TX_PKT)) {
qdrv_show_debug_level("TX pkt");
} else {
printk("TX pkt dump disabled.\n");
}
} else if (strcmp(argv[2], "vendor_fix") == 0) {
uint32_t vendor_fix = ic->ic_vendor_fix;
qdrv_control_set_show(qdrv_show_vendor_fix, (void *) qw, 1, 1);
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX, "Current vendor fix flag is 0x%x\n",
ic->ic_vendor_fix);
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"DHCP fix is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_DHCP) ? "enabled" : "disabled");
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"Replace IGMP src mac is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_REPLACE_IGMP_SRCMAC) ? "enabled" : "disabled");
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"Replace IP src mac is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_REPLACE_IP_SRCMAC) ? "enabled" : "disabled");
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_PKT_RX,"Drop STA IGMP query is %s\n",
(vendor_fix & VENDOR_FIX_BRCM_DROP_STA_IGMPQUERY) ? "enabled" : "disabled");
} else if (strcmp(argv[2], "hw_options") == 0) {
qcb->value_from_muc = qdrv_soc_get_hw_options();
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "hw_desc") == 0) {
qdrv_control_set_show(qdrv_show_hw_desc, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "rf_chipid") == 0) {
qcb->value_from_muc = qw->rf_chipid;
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "rf_chip_verid") == 0) {
qcb->value_from_muc = qw->rf_chip_verid;
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "auc_stats") == 0) {
qdrv_control_set_show(qdrv_show_auc_stats, (void *)mac, 1, 1);
} else if (strcmp(argv[2], "dsp_stats") == 0) {
qdrv_control_set_show(qdrv_show_dsp_stats, (void *)mac, 1, 1);
} else if (strcmp(argv[2], "uc_tx_stats") == 0) {
qdrv_control_set_show(qdrv_show_uc_tx_stats, (void *)mac, 1, 1);
} else if (strcmp(argv[2], "uc_rx_stats") == 0) {
qdrv_control_set_show(qdrv_show_uc_rx_stats, (void *)mac, 1, 1);
} else if (strcmp(argv[2], "wmm_ac_map") == 0) {
qdrv_control_set_show(qdrv_control_show_wmm_ac_map, (void *)mac, 1, 1);
} else if (strcmp(argv[2], "br_isolate") == 0) {
qdrv_control_set_show(qdrv_control_get_br_isolate, (void *)mac, 1, 1);
} else if (strcmp(argv[2], "power_table_checksum") == 0) {
struct qdrv_power_table_checksum_entry *p_entry = qcb->power_table_ctrl.checksum_list;
while (p_entry) {
if (strcmp(argv[3], p_entry->fname) == 0) {
break;
}
p_entry = p_entry->next;
}
qcb->power_table_ctrl.reading_checksum = p_entry;
qdrv_control_set_show(qdrv_show_checksum, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "checksum_list") == 0) {
struct qdrv_power_table_checksum_entry *p_entry = qcb->power_table_ctrl.checksum_list;
while (p_entry) {
printk("%s %s\n", p_entry->checksum, p_entry->fname);
p_entry = p_entry->next;
}
} else if (strcmp(argv[2], "power_selection") == 0) {
qcb->value_from_muc = qcb->power_table_ctrl.power_selection;
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "power_recheck") == 0) {
qcb->value_from_muc = qcb->power_table_ctrl.power_recheck;
qdrv_control_set_show(qdrv_show_muc_value, (void *) qcb, 1, 1);
} else if (strcmp(argv[2], "vlan_config") == 0) {
if (argc != 4)
return -1;
qdrv_control_set_show(qdrv_control_show_vlan_config, (void *)argv[3], 1, 1);
} else if(strcmp(argv[2], "mac_reserve") == 0) {
if (argc != 4)
return -1;
qdrv_control_set_show(qdrv_mac_reserve_show, argv[3], 1, 1);
} else if (strcmp(argv[2], "wbsp_ctrl") == 0) {
printk("qdrv_wbsp_ctrl = %d\n", qdrv_wbsp_ctrl);
} else if (strcmp(argv[2], "vap_default_state") == 0) {
qdrv_control_set_show(qdrv_show_vap_default_state, (void *) qw, 1, 1);
} else if (strcmp(argv[2], "core_dump_size") == 0) {
qdrv_control_set_show(qdrv_show_core_dump_size, &qdrv_crash_log_len, 1, 1);
} else if (strcmp(argv[2], "core_dump") == 0) {
if (!qdrv_crash_log) {
DBGPRINTF_E("QDRV: core dump not saved\n");
} else {
qdrv_control_set_show(qdrv_show_core_dump, qdrv_crash_log, qdrv_crash_log_len,
qdrv_crash_log_len);
}
} else if (strcmp(argv[2], "wps_intf") == 0) {
qdrv_control_set_show(qdrv_show_wps_intf, (void *)qw, 1, 1);
#ifdef CONFIG_NAC_MONITOR
} else if(strcmp(argv[2], "nac_info") == 0) {
qdrv_wlan_get_nac_info(qw);
#endif
} else {
DBGPRINTF_E("The get request \"%s\" is unknown.\n", argv[2]);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
static int read_proc_carrier_id(struct seq_file *s)
{
seq_printf(s, "%d\n",g_carrier_id);
return (0);
}
#else
static int read_proc_carrier_id(char *page, char **start, off_t offset, int count, int *eof, void *data)
{
int len;
char *p = page;
if (offset > 0) {
*eof = 1;
return 0;
}
p += sprintf(p, "%d\n",g_carrier_id);
len = p - page;
return len;
}
#endif
#if defined(QTN_DEBUG)
static int qdrv_command_dbg(struct device *dev, int argc, char *argv[])
{
u32 module_id = 0;
u32 dbg_func_mask = 0;
u32 dbg_log_level = 0;
u32 arg = 0;
int i = 0;
if (argc < 2) {
goto error;
}
if (strcmp(argv[1], "set") == 0) {
if (argc < 5) {
goto error;
}
for (i = 0; i < (DBG_LM_MAX - 1); i++) {
if (!strcmp(argv[2], dbg_module_name_entry[i].dbg_module_name)){
module_id = dbg_module_name_entry[i].dbg_module_id;
break;
}
}
if (module_id == 0) {
goto error;
}
if (sscanf(argv[3], "%x", &dbg_func_mask) != 1) {
goto error;
}
if (sscanf(argv[4], "%u", &dbg_log_level) != 1) {
goto error;
}
if (module_id != DBG_LM_QMACFW) {
g_dbg_log_module |= BIT(module_id - 1);
g_dbg_log_func[module_id - 1] = dbg_func_mask;
g_dbg_log_level[module_id - 1] = dbg_log_level;
} else {
struct qdrv_cb *qcb;
qcb = dev_get_drvdata(dev);
struct qdrv_mac *mac = &qcb->macs[0];
struct qdrv_wlan *qw = (struct qdrv_wlan *) mac->data;
arg = dbg_func_mask << 4 | dbg_log_level;
qdrv_hostlink_msg_cmd(qw, IOCTL_DEV_CMD_SET_DRV_DBG, arg);
}
} else if (strcmp(argv[1], "get") == 0) {
for (i = 0; i < (DBG_LM_MAX - 1); i++) {
if (!strcmp(argv[2], dbg_module_name_entry[i].dbg_module_name)) {
module_id = dbg_module_name_entry[i].dbg_module_id;
break;
}
}
if (module_id == 0){
goto error;
}
if (module_id != DBG_LM_QMACFW) {
printk("module name: %s\n", argv[2]);
printk("function mask: 0x%08x\n", g_dbg_log_func[module_id - 1]);
printk("debug level: %u\n", g_dbg_log_level[module_id - 1]);
} else {
struct qdrv_cb *qcb;
qcb = dev_get_drvdata(dev);
struct qdrv_mac *mac = &qcb->macs[0];
struct qdrv_wlan *qw = (struct qdrv_wlan *) mac->data;
qdrv_hostlink_msg_cmd(qw, IOCTL_DEV_CMD_GET_DRV_DBG, 0);
}
}
return(0);
error:
printk("Usage\n");
printk(" dbg set <module name> <function mask> <debug level>\n");
printk(" dbg get <module name>\n");
printk("Module names:\n");
for (i = 0; i < (DBG_LM_MAX - 1); i++) {
printk(" %s\n", dbg_module_name_entry[i].dbg_module_name);
}
return(0);
}
#endif
static void qdrv_command_pktlogger_usage (void) {
printk("Usage:\n"
" pktlogger 0 show\n"
" pktlogger 0 start <log type> [<interval>]\n"
" pktlogger 0 stop <log type>\n"
" pktlogger 0 set <parameter> <value>\n"
"\n"
"Parameters:\n"
" show display current settings\n"
" start start logging for the specified log type\n"
" <log type> stats, radar, txbf, iwevent, sysmsg, mem, vsp, phy_stats, dsp_stats, core_dump\n"
" <interval> logging frequency\n"
" stop stop logging for the specified log type\n"
" set set a parameter value\n"
" <parameter> dstmac, dstip, dstport, srcip, wifimac, interface\n");
}
static int qdrv_command_pktlogger(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac;
struct qdrv_wlan *qw;
int ret = 0;
if (argc < 2) {
qdrv_command_pktlogger_usage();
return 0;
}
mac = qdrv_control_mac_get(dev, argv[1]);
if (mac == NULL) {
DBGPRINTF_E("mac NULL\n");
return -1;
}
qw = qdrv_control_wlan_get(mac);
if (!qw) {
return -1;
}
if (strcmp(argv[2], "set") == 0) {
if (argc < 5) {
qdrv_command_pktlogger_usage();
return 0;
}
ret = qdrv_pktlogger_set(qw, argv[3], argv[4]);
} else if (strcmp(argv[2], "show") == 0) {
qdrv_pktlogger_show(qw);
} else if (strcmp(argv[2], "start") == 0) {
unsigned interval = 0;
if (argc < 4) {
qdrv_command_pktlogger_usage();
return 0;
}
if (argc >= 5) {
if (sscanf(argv[4], "%d", &interval) != 1) {
return 0;
}
}
ret = qdrv_pktlogger_start_or_stop(qw, argv[3], 1, interval);
} else if (strcmp(argv[2], "stop") == 0) {
if (argc < 4) {
qdrv_command_pktlogger_usage();
return 0;
}
ret = qdrv_pktlogger_start_or_stop(qw, argv[3], 0, 0);
} else {
qdrv_command_pktlogger_usage();
}
return ret;
}
static int qdrv_command_rf_reg_dump(struct device *dev, int argc, char *argv[])
{
struct qdrv_mac *mac;
struct qdrv_wlan *qw;
u_int32_t arg = 0;
if (argc < 2) {
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;
}
qdrv_hostlink_msg_cmd(qw, IOCTL_DEV_CMD_RF_REG_DUMP, arg);
return 0;
}
int qdrv_control_output(struct device *dev, char *buf)
{
struct qdrv_cb *qcb;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Get the private device data */
qcb = dev_get_drvdata(dev);
/* Show the return code from the previous command */
if(qcb->rc == 0)
{
strcpy(buf, "ok\n");
}
else
{
sprintf(buf, "error %d\n", qcb->rc);
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(strlen(buf));
}
static int qdrv_parse_args(char *string, int argc, char *argv[])
{
char *p = string;
int n = 0;
char *end;
int i;
/* Skip leading white space up to first argument */
while((*p != '\0') && isspace(*p) && p++);
/* Check for empty string */
if (*p == '\0') {
/* Empty string!! */
return(0);
}
for (i = 0; i < argc; i++) {
/* Save argument */
argv[i] = p;
/* Increment the number of arguments we have found */
n++;
/* Skip argument */
while ((*p != '\0') && !isspace(*p) && p++);
/* Remember the end of argument */
end = p;
/* Skip leading white spaces up to next argument(s) */
while ((*p != '\0') && isspace(*p) && p++);
/* Terminate argument */
*end = '\0';
/* Check for end of arguments */
if (*p == '\0') {
break;
}
}
/* Check if there are arguments left */
if (*p != '\0') {
/* Too many arguments */
return(-1);
}
return(n);
}
int qdrv_control_input(struct device *dev, char *buf, unsigned int count)
{
int n;
int found = -1;
struct qdrv_cb *qcb;
char *argv[19];
int argc;
char *p;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Get the private device data */
qcb = dev_get_drvdata(dev);
if (!qcb) {
return -1;
}
/* Make sure it fits into our command buffer as a '\0' */
/* terminated string. */
if (count >= (sizeof(qcb->command) - 1)) {
DBGPRINTF_E("Command is too large (%d >= %d)\n",
count, sizeof(qcb->command) - 1);
qcb->rc = -1;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
/* Copy to a buffer to make a proper C string */
memcpy(qcb->command, buf, count);
qcb->command[count] = '\0';
/* Kill '\n' if there is one */
p = strrchr(qcb->command, '\n');
if (p) {
*p = '\0';
}
/* Parse the arguments */
argc = qdrv_parse_args(qcb->command, 23, argv);
/* Make sure we got at least a command */
if (argc == 0) {
DBGPRINTF_E("No command specified\n");
qcb->rc = -2;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
if (argc < 0) {
DBGPRINTF_E("Too many arguments specified.\n");
qcb->rc = -3;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
/* Try to match the commands to the input */
for (n = 0; found < 0 && n < COMMAND_TABLE_SIZE; n++) {
if (strcmp(argv[0], s_command_table[n].command) == 0) {
found = n;
break;
}
}
/* Call the function if we found a match */
if (found >= 0) {
if ((*s_command_table[found].fn)(dev, argc, argv) < 0) {
DBGPRINTF_E("Failed to execute command \"%s\" (%d)\n",
s_command_table[found].command, found);
qcb->rc = -4;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
} else {
DBGPRINTF_E("Command \"%s\" is not recognized\n",qcb->command);
qcb->rc = -5;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
qcb->rc = 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
void qdrv_control_set_show(void (*fn)(struct seq_file *s, void *data, u32 num),
void *data, int start_num, int decr)
{
if (data == NULL) {
DBGPRINTF_E("qdrv_control_set_show called with NULL address\n" );
return;
}
if (down_interruptible(&s_output_sem)) {
return;
}
if (s_output_qcb == NULL) {
/* Nothing to output */
up(&s_output_sem);
return;
}
/* Get the number of items to read */
s_output_qcb->read_start_num = start_num;
s_output_qcb->read_num = start_num;
s_output_qcb->read_decr = decr;
s_output_qcb->read_data = data;
s_output_qcb->read_show = fn;
up(&s_output_sem);
return;
}
static void *qdrv_seq_start(struct seq_file *s, loff_t *pos)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (down_interruptible(&s_output_sem)) {
return NULL;
}
if (s_output_qcb == NULL) {
/* Nothing to output */
up(&s_output_sem);
return NULL;
}
up(&s_output_sem);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
/* Return the number of items to read */
if (*pos >= 0 && *pos <= s_output_qcb->read_start_num / s_output_qcb->read_decr) {
s_output_qcb->read_num = s_output_qcb->read_start_num -
*pos * s_output_qcb->read_decr;
return (void*)s_output_qcb->read_num;
} else {
return NULL;
}
}
static void *qdrv_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (down_interruptible(&s_output_sem)) {
return NULL;
}
if (s_output_qcb == NULL) {
/* Nothing to output */
up(&s_output_sem);
return NULL;
}
(*pos)++;
s_output_qcb->read_num -= s_output_qcb->read_decr;
if (s_output_qcb->read_num <= 0) {
/* The iterator is done */
up(&s_output_sem);
return NULL;
}
up(&s_output_sem);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return (void*)s_output_qcb->read_num;
}
static void qdrv_seq_stop(struct seq_file *s, void *v)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (down_interruptible(&s_output_sem)) {
return;
}
if (s_output_qcb == NULL) {
/* Not valid any more */
up(&s_output_sem);
return;
}
if (v == NULL)
s_output_qcb->read_show = NULL;
up(&s_output_sem);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return;
}
static int qdrv_seq_show(struct seq_file *s, void *v)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (down_interruptible(&s_output_sem)) {
return -ERESTARTSYS;
}
if (s_output_qcb == NULL) {
/* Nothing to output */
up(&s_output_sem);
return -1;
}
if (s_output_qcb->read_show != NULL) {
(*s_output_qcb->read_show)(s, s_output_qcb->read_data, (u32) v);
}
up(&s_output_sem);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return 0;
}
/*
* MU related commands:
* syntax: mu {set | get | dbg | clr} [sub-cmd] [mac_addr] [options]
*
* 1. set station group id and user_position
* mu set grp {mac_addr} {grp_id} {position}
* 2. clear station group id
* mu clr grp {mac_addr} {grp_id} {position}
*/
static int qdrv_command_mu(struct device *dev, int argc, char *argv[])
{
int i;
qdrv_mu_cmd mu_cmd;
uint8_t mac[IEEE80211_ADDR_LEN];
uint8_t grp = 0, delete = 0;
struct ieee80211_node *ni = NULL, *ni1 = NULL;
struct ieee80211com *ic = qdrv_get_ieee80211com(dev);
int res = -1;
if (!ieee80211_swfeat_is_supported(SWFEAT_ID_MU_MIMO, 1))
return -1;
for (i = 0; i < argc; i++) {
printk("arg[%d] %s\n", i, argv[i]);
}
/* parse the cmd chains */
if (strcmp(argv[1], "set") == 0) {
mu_cmd = QDRV_MU_CMD_SET;
} else if (strcmp(argv[1], "get") == 0) {
mu_cmd = QDRV_MU_CMD_GET;
} else if (strcmp(argv[1], "dbg") == 0) {
mu_cmd = QDRV_MU_CMD_DBG;
} else if (strcmp(argv[1], "clr") == 0) {
mu_cmd = QDRV_MU_CMD_CLR;
delete = 1;
} else if (strcmp(argv[1], "sta0") == 0) {
mu_cmd = QDRV_MU_CMD_FIRST_IN_GROUP_SELECTION;
} else {
goto mu_exit;
}
if (mu_cmd == QDRV_MU_CMD_SET || mu_cmd == QDRV_MU_CMD_GET) {
if (qdrv_parse_mac(argv[3], mac) < 0) {
printk("Error mac address\n");
goto mu_exit;
}
ni = ieee80211_find_node(&ic->ic_sta, mac);
if (!ni) {
printk("Can't find node\n");
goto mu_exit;
}
} else if (mu_cmd == QDRV_MU_CMD_FIRST_IN_GROUP_SELECTION) {
if (qdrv_parse_mac(argv[2], mac) < 0) {
printk("Error mac address\n");
goto mu_exit;
}
}
/* parse subcmd & the parameters */
switch (mu_cmd) {
case QDRV_MU_CMD_CLR:
{
if (argc < 4) {
goto mu_exit;
}
volatile struct qtn_txbf_mbox *txbf_mbox = qtn_txbf_mbox_get();
grp = _atoi(argv[3]);
if (!(grp > 0 && grp < ARRAY_SIZE(txbf_mbox->mu_grp_man_rank) + 1)) {
printk("Group %u is out of range\n", grp);
goto mu_exit;
}
grp--;
txbf_mbox->mu_grp_man_rank[grp].u0_aid = 0;
txbf_mbox->mu_grp_man_rank[grp].u1_aid = 0;
txbf_mbox->mu_grp_man_rank[grp].rank = 0;
break;
}
case QDRV_MU_CMD_GET:
printk("MU grp: "
"%02x%02x%02x%02x%02x%02x%02x%02x\n"
"MU pos: %02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x\n",
ni->ni_mu_grp.member[7],
ni->ni_mu_grp.member[6],
ni->ni_mu_grp.member[5],
ni->ni_mu_grp.member[4],
ni->ni_mu_grp.member[3],
ni->ni_mu_grp.member[2],
ni->ni_mu_grp.member[1],
ni->ni_mu_grp.member[0],
ni->ni_mu_grp.pos[15],
ni->ni_mu_grp.pos[14],
ni->ni_mu_grp.pos[13],
ni->ni_mu_grp.pos[12],
ni->ni_mu_grp.pos[11],
ni->ni_mu_grp.pos[10],
ni->ni_mu_grp.pos[9],
ni->ni_mu_grp.pos[8],
ni->ni_mu_grp.pos[7],
ni->ni_mu_grp.pos[6],
ni->ni_mu_grp.pos[5],
ni->ni_mu_grp.pos[4],
ni->ni_mu_grp.pos[3],
ni->ni_mu_grp.pos[2],
ni->ni_mu_grp.pos[1],
ni->ni_mu_grp.pos[0]);
break;
case QDRV_MU_CMD_SET:
{
if (argc < 7) {
goto mu_exit;
}
if (qdrv_parse_mac(argv[4], mac) < 0) {
printk("Error mac address\n");
goto mu_exit;
}
ni1 = ieee80211_find_node(&ic->ic_sta, mac);
if (!ni1) {
printk("Can't find node\n");
goto mu_exit;
}
int32_t rank = _atoi(argv[6]);
volatile struct qtn_txbf_mbox *txbf_mbox = qtn_txbf_mbox_get();
grp = _atoi(argv[5]);
if (!(grp > 0 && grp < ARRAY_SIZE(txbf_mbox->mu_grp_man_rank) + 1)) {
printk("Group %u is out of range\n", grp);
goto mu_exit;
}
grp--;
txbf_mbox->mu_grp_man_rank[grp].u0_aid= IEEE80211_AID(ni->ni_associd);
txbf_mbox->mu_grp_man_rank[grp].u1_aid= IEEE80211_AID(ni1->ni_associd);
txbf_mbox->mu_grp_man_rank[grp].rank = rank;
break;
}
case QDRV_MU_CMD_FIRST_IN_GROUP_SELECTION:
{
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
if (vap) {
ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_FIRST_STA_IN_MU_SOUNDING, 0,
mac, sizeof(mac));
}
break;
}
default:
goto mu_exit;
break;
}
res = 0;
mu_exit:
if (res == -1) {
printk("Usage: mu {set | get | clr | dbg | sta0} [sub-cmd] [mac_addr] [options]\n");
}
if (ni) {
ieee80211_free_node(ni);
}
if (ni1) {
ieee80211_free_node(ni1);
}
return(0);
}
static struct seq_operations s_qdrv_seq_ops =
{
.start = qdrv_seq_start,
.next = qdrv_seq_next,
.stop = qdrv_seq_stop,
.show = qdrv_seq_show,
};
static int qdrv_proc_open(struct inode *inode, struct file *file)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(seq_open(file, &s_qdrv_seq_ops));
}
static struct file_operations s_qdrv_proc_ops =
{
.owner = THIS_MODULE,
.open = qdrv_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
static int carrier_id_seq_show(struct seq_file *file, void *v)
{
return read_proc_carrier_id(file);
}
static int carrier_id_open(struct inode *inode, struct file *file)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return single_open(file, carrier_id_seq_show, NULL);
}
static struct file_operations s_carrier_id_ops = {
.owner = THIS_MODULE,
.open = carrier_id_open,
.read = seq_read,
.llseek = seq_lseek,
.release= seq_release,
};
#endif
int qdrv_control_init(struct device *dev)
{
struct qdrv_cb *qcb;
struct proc_dir_entry *entry, *carrier_entry;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF_N("qdrv wbsp: %d\n", qdrv_wbsp_ctrl);
/* Get the private device data */
qcb = dev_get_drvdata(dev);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
if ((entry = proc_create_data("qdrvdata", S_IFREG | 0666, NULL, &s_qdrv_proc_ops, NULL)) == NULL) {
DBGPRINTF_E("Failed to create \"/proc/qdrvdata\"\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
goto err1;
}
#else
if ((entry = create_proc_entry("qdrvdata", S_IFREG | 0666, NULL)) == NULL) {
DBGPRINTF_E("Failed to create \"/proc/qdrvdata\"\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
sema_init(&s_output_sem, 1);
#else
init_MUTEX(&s_output_sem);
#endif
s_output_qcb = qcb;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
entry->proc_fops = &s_qdrv_proc_ops;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
carrier_entry = proc_create_data("carrier_id", 0, NULL, &s_carrier_id_ops, NULL);
if (carrier_entry == NULL) {
DBGPRINTF_E("Failed to create \"/proc/carrier_id\"\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
goto err2;
}
#else
carrier_entry = create_proc_read_entry("carrier_id", 0, NULL, read_proc_carrier_id, NULL);
if (carrier_entry == NULL) {
DBGPRINTF_E("Failed to create \"/proc/carrier_id\"\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-1);
}
#endif
spin_lock_init(&qdrv_event_lock);
memset(qdrv_event_log_table,0,sizeof(qdrv_event_log_table));
QDRV_EVENT("Qdrv Log Init");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
err2:
remove_proc_entry("qdrvdata", NULL);
err1:
return -1;
}
int qdrv_control_exit(struct device *dev)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
if (down_interruptible(&s_output_sem)) {
return(-ERESTARTSYS);
}
s_output_qcb = NULL;
up(&s_output_sem);
remove_proc_entry("qdrvdata", NULL);
remove_proc_entry("hw_revision", NULL);
remove_proc_entry("carrier_id", NULL);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}