blob: dd95d5d288a5da96a36500c5709cd3dc3cf34ae9 [file] [log] [blame]
/**
* Copyright (c) 2012-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 <linux/device.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
#include <linux/proc_fs.h>
#endif
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/elf.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include "qdrv_features.h"
#include "qdrv_debug.h"
#include "qdrv_mac.h"
#include "qdrv_soc.h"
#include "qdrv_muc.h"
#include "qdrv_hal.h"
#include "qdrv_wlan.h"
#include <qtn/txbf_mbox.h>
#define QDRV_MU_PROC_FILENAME "qdrv_mu"
static struct qdrv_cb *qdrv_mu_qcb = NULL;
static const char* status_str[] = {"Enabled", "Disabled", "Freezed", "Not used"};
enum mu_grp_status {
MU_GRP_STR_EN = 0,
MU_GRP_STR_DIS = 1,
MU_GRP_STR_FRZ = 2,
MU_GRP_STR_NUS = 3,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
static int proc_qdrv_mu_stat_show(struct seq_file *sfile, void *v)
{
struct qdrv_wlan *qw = qdrv_mu_qcb->macs[0].data;
struct ieee80211com *ic;
struct ieee80211vap *vap;
struct qtn_mu_grp_args mu_grp_tbl[IEEE80211_MU_GRP_NUM_MAX];
bool is_station = false;
int i;
int is_mu = 0;
int j;
if (qw == NULL)
return -EFAULT;
ic = &qw->ic;
if (ic == NULL)
return -EFAULT;
vap = TAILQ_FIRST(&qw->ic.ic_vaps);
if (vap->iv_opmode == IEEE80211_M_STA)
is_station = true;
ieee80211_get_mu_grp(ic, &mu_grp_tbl[0]);
for (i = IEEE80211_VHT_GRP_1ST_BIT_OFFSET; i <= IEEE80211_VHT_GRP_MAX_BIT_OFFSET; i++) {
if (mu_grp_tbl[i].grp_id == i) {
is_mu = 1;
if (!is_station) {
enum mu_grp_status idx = MU_GRP_STR_DIS;
if (DSP_PARAM_GET(debug_flag) & MU_QMAT_FREEZE) {
idx = MU_GRP_STR_FRZ;
} else if (mu_grp_tbl[i].qmat_installed == MU_QMAT_ENABLED) {
idx = MU_GRP_STR_EN;
} else if (mu_grp_tbl[i].qmat_installed == MU_QMAT_NOT_USED) {
idx = MU_GRP_STR_NUS;
}
seq_printf(sfile, "GRP ID: %d update cnt %d", i, mu_grp_tbl[i].upd_cnt);
seq_printf(sfile, " %s\n", status_str[idx]);
seq_printf(sfile, "Rank: %d\n", mu_grp_tbl[i].rank);
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].aid); j++) {
if ( mu_grp_tbl[i].aid[j] != 0) {
seq_printf(sfile, "AID%d: 0x%04x ", j, mu_grp_tbl[i].aid[j]);
}
}
seq_printf(sfile, "\n");
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].ncidx); j++) {
if ( mu_grp_tbl[i].ncidx[j] != 0) {
seq_printf(sfile, "IDX%d: %4d ", j, mu_grp_tbl[i].ncidx[j]);
}
}
seq_printf(sfile, "\n");
if(mu_grp_tbl[i].qmat_installed == MU_QMAT_ENABLED ||
mu_grp_tbl[i].qmat_installed == MU_QMAT_FREEZED) {
seq_printf(sfile, "u0_1ss_u1_1ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_1ss);
seq_printf(sfile, "u0_2ss_u1_1ss: 0x%x\n", mu_grp_tbl[i].u0_2ss_u1_1ss);
seq_printf(sfile, "u0_3ss_u1_1ss: 0x%x\n", mu_grp_tbl[i].u0_3ss_u1_1ss);
seq_printf(sfile, "u0_1ss_u1_2ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_2ss);
seq_printf(sfile, "u0_1ss_u1_3ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_3ss);
seq_printf(sfile, "u0_2ss_u1_2ss: 0x%x\n", mu_grp_tbl[i].u0_2ss_u1_2ss);
#ifdef PEARL_PLATFORM
seq_printf(sfile, "u0_1ss_u1_1ss_u2_1ss_u3_1ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_1ss_u2_1ss_u3_1ss);
#endif
}
} else {
seq_printf(sfile, "AP GRP ID: %d update cnt %d\n", i, mu_grp_tbl[i].upd_cnt);
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].aid); j++) {
if ( mu_grp_tbl[i].aid[j] != 0) {
seq_printf(sfile, "User pos = %d with AID = 0x%04x\n", j, mu_grp_tbl[i].aid[j]);
}
}
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].ncidx); j++) {
if ( mu_grp_tbl[i].ncidx[j] != 0) {
seq_printf(sfile, "Local node index (Idx) = %d\n", mu_grp_tbl[i].ncidx[j]);
}
}
}
}
}
if (!is_mu) {
seq_printf(sfile, "No MU groups found\n");
}
return 0;
}
static int proc_qdrv_mu_stat_open(struct inode *inode, struct file *file)
{
return single_open(file, proc_qdrv_mu_stat_show, NULL);
}
static const struct file_operations proc_qdrv_mu_stat_ops = {
.owner = THIS_MODULE,
.open = proc_qdrv_mu_stat_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#else
static int qdrv_mu_stat_rd(char *page, char **start, off_t offset,
int count, int *eof, void *data)
{
struct qdrv_wlan *qw = qdrv_mu_qcb->macs[0].data;
bool is_station = false;
if (qw == NULL) {
return -EFAULT;
}
struct ieee80211vap* vap = TAILQ_FIRST(&qw->ic.ic_vaps);
if (vap->iv_opmode == IEEE80211_M_STA) {
is_station = true;
}
struct ieee80211com* ic = &qw->ic;
if (ic == NULL) {
return -EFAULT;
}
char *p = page;
struct qtn_mu_grp_args mu_grp_tbl[IEEE80211_MU_GRP_NUM_MAX];
ieee80211_get_mu_grp(ic, &mu_grp_tbl[0]);
int i, is_mu = 0;
for (i = IEEE80211_VHT_GRP_1ST_BIT_OFFSET; i <= IEEE80211_VHT_GRP_MAX_BIT_OFFSET; i++) {
if (mu_grp_tbl[i].grp_id == i) {
is_mu = 1;
int j;
if (!is_station)
{
enum mu_grp_status idx = MU_GRP_STR_DIS;
if (DSP_PARAM_GET(debug_flag) & MU_QMAT_FREEZE) {
idx = MU_GRP_STR_FRZ;
} else if (mu_grp_tbl[i].qmat_installed == MU_QMAT_ENABLED) {
idx = MU_GRP_STR_EN;
} else if (mu_grp_tbl[i].qmat_installed == MU_QMAT_NOT_USED) {
idx = MU_GRP_STR_NUS;
}
p += sprintf(p, "GRP ID: %d update cnt %d", i, mu_grp_tbl[i].upd_cnt);
p += sprintf(p, " %s\n", status_str[idx]);
p += sprintf(p, "Rank: %d\n", mu_grp_tbl[i].rank);
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].aid); j++) {
if ( mu_grp_tbl[i].aid[j] != 0) {
p += sprintf(p, "AID%d: 0x%04x ", j, mu_grp_tbl[i].aid[j]);
}
}
p += sprintf(p, "\n");
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].ncidx); j++) {
if ( mu_grp_tbl[i].ncidx[j] != 0) {
p += sprintf(p, "IDX%d: %4d ", j, mu_grp_tbl[i].ncidx[j]);
}
}
p += sprintf(p, "\n");
if(mu_grp_tbl[i].qmat_installed == MU_QMAT_ENABLED ||
mu_grp_tbl[i].qmat_installed == MU_QMAT_FREEZED) {
p += sprintf(p, "u0_1ss_u1_1ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_1ss);
p += sprintf(p, "u0_2ss_u1_1ss: 0x%x\n", mu_grp_tbl[i].u0_2ss_u1_1ss);
p += sprintf(p, "u0_3ss_u1_1ss: 0x%x\n", mu_grp_tbl[i].u0_3ss_u1_1ss);
p += sprintf(p, "u0_1ss_u1_2ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_2ss);
p += sprintf(p, "u0_1ss_u1_3ss: 0x%x\n", mu_grp_tbl[i].u0_1ss_u1_3ss);
p += sprintf(p, "u0_2ss_u1_2ss: 0x%x\n", mu_grp_tbl[i].u0_2ss_u1_2ss);
}
}
else
{
p += sprintf(p, "AP GRP ID: %d update cnt %d\n", i, mu_grp_tbl[i].upd_cnt);
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].aid); j++) {
if ( mu_grp_tbl[i].aid[j] != 0) {
p += sprintf(p, "User pos = %d with AID = 0x%04x\n", j, mu_grp_tbl[i].aid[j]);
}
}
for (j = 0; j < ARRAY_SIZE(mu_grp_tbl[i].ncidx); j++) {
if ( mu_grp_tbl[i].ncidx[j] != 0) {
p += sprintf(p, "Local node index (Idx) = %d\n", mu_grp_tbl[i].ncidx[j]);
}
}
}
}
}
if (!is_mu) {
p += sprintf(p, "No MU groups found\n");
}
return p - page;
}
#endif
int qdrv_mu_stat_init(struct qdrv_cb *qcb)
{
printk("qdrv_mu_stat_init\n");
if (qcb == NULL) {
printk("qdrv_mu_stat_init: NULL qcb\n");
return -EFAULT;
}
qdrv_mu_qcb = qcb;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
if (!proc_create_data(QDRV_MU_PROC_FILENAME, 0, NULL,
&proc_qdrv_mu_stat_ops, NULL)) {
return -EEXIST;
}
#else
if (!create_proc_read_entry(QDRV_MU_PROC_FILENAME, 0,
NULL, qdrv_mu_stat_rd, NULL)) {
return -EEXIST;
}
#endif
return 0;
}
int qdrv_mu_stat_exit(struct qdrv_cb *qcb)
{
if (qdrv_mu_qcb != NULL) {
remove_proc_entry(QDRV_MU_PROC_FILENAME, 0);
}
return 0;
}