blob: decb073f54228f6cd7a09d2cb8ab31fb77a9bf9f [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 <linux/device.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include "qdrv_features.h"
#include "qdrv_debug.h"
#include "qdrv_mac.h"
#include "qdrv_soc.h"
#include "qdrv_comm.h"
#include "qdrv_wlan.h"
#include "qdrv_vap.h"
#include <qtn/qtn_global.h>
#ifdef MTEST
#include "../mtest/mtest.h"
#endif
static void dump_hring(struct qdrv_wlan *qw)
{
int i = 0;
struct host_ioctl *ioctl;
struct host_ioctl *ioctl_phys;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_HLINK,
"HLINK State Write %d Tosend %d Read %d First %p Last %p Mbx %p\n",
qw->tx_if.hl_write, qw->tx_if.hl_tosend, qw->tx_if.hl_read,
qw->tx_if.hl_first, qw->tx_if.hl_last,&qw->tx_if.tx_mbox[0]);
for (i = 0; i < QNET_HLRING_ENTRIES; i++) {
ioctl = &qw->tx_if.hl_ring[i];
ioctl_phys = &(((struct host_ioctl *) qw->tx_if.hl_ring_dma)[qw->tx_if.hl_tosend]);
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_HLINK,
"I[%d] IO(V):%p IO(P):%p ARGP:%p COMM:%d STAT:%08X RC:%08X\n", i,
ioctl, ioctl_phys,(void *)ioctl->ioctl_argp,
ioctl->ioctl_command,
ioctl->ioctl_status,
ioctl->ioctl_rc);
}
}
static struct host_ioctl *qdrv_alloc_ioctl(struct qdrv_wlan *qw)
{
int indx;
struct host_ioctl *ioctl;
unsigned long flags;
#ifdef QDRV_FEATURE_KILL_MUC
if (qw->flags_ext & QDRV_WLAN_MUC_KILLED) {
return NULL;
}
#endif
spin_lock_irqsave(&qw->tx_if.hl_flowlock, flags);
/* Search for an empty IOCTL */
for (indx=0; indx < QNET_HLRING_ENTRIES; indx++) {
ioctl = &qw->tx_if.hl_ring[indx];
if (ioctl->ioctl_status == QTN_HLINK_STATUS_AVAIL)
break;
}
if (indx == QNET_HLRING_ENTRIES) {
DBGPRINTF_E("Hostlink buffer not available\n");
if (DBG_LOG_FUNC_TEST(QDRV_LF_HLINK)) {
dump_hring(qw);
}
spin_unlock_irqrestore(&qw->tx_if.hl_flowlock, flags);
return (NULL);
}
ioctl->ioctl_status = 0;
spin_unlock_irqrestore(&qw->tx_if.hl_flowlock, flags);
memset(ioctl, 0, sizeof(*ioctl));
ioctl->ioctl_dev = qw->unit;
return (ioctl);
}
static void qdrv_free_ioctl(struct host_ioctl *ioctl)
{
if (ioctl == NULL) {
return;
}
ioctl->ioctl_status = QTN_HLINK_STATUS_AVAIL;
}
#define QDRV_IOCTL_EVNT_TIMEOUT(cond, wait_time, msg, status, proc_context) \
do { \
unsigned long start_time = jiffies; \
u32 timeout = 0; \
u32 dly_cnt = 0; \
u32 irq_to = wait_time*100000/HZ; \
for (; !timeout && cond;) { \
if (proc_context) { \
msleep(1); \
} \
if (in_irq()) { \
udelay(10); \
timeout = (++dly_cnt) > irq_to; \
} else { \
timeout = time_after(jiffies, start_time + wait_time); \
} \
} \
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_HLINK, \
"HLINK MSG: %s %s after %lu jiffies\n", \
msg, timeout ? "timed out" : "accepted", jiffies - start_time); \
status = timeout && cond; \
} while (0);
#define QDRV_IOCTL_RET_TIMEOUT (-1)
#define QDRV_IOCTL_HARD_IRQ_PM_WAIT_TIME 30
#define QDRV_IOCTL_HARD_IRQ_WAIT_TIME (HZ / 20)
#define QDRV_IOCTL_ISR_WAIT_TIME (HZ / 2)
#define QDRV_IOCTL_PROC_WAIT_TIME (HZ * 10)
static int qdrv_send_ioctl(struct qdrv_wlan *qw, struct host_ioctl *ioctl)
{
#ifdef MTEST
return 0;
#else
volatile u32 *mbox = &qw->tx_if.tx_mbox[0];
struct qdrv_mac *mac = qw->mac;
struct ieee80211com *ic = &qw->ic;
int rc;
int in_isr = 0;
int wait_time;
unsigned long flags;
char *desc;
/* This is a purely blocking IOCTL. No real ring. */
KASSERT(ioctl, (DBGEFMT "PASSED NULL IOCTL IN HOSTLINK SEND", DBGARG));
in_isr = in_interrupt();
if (in_irq()) {
wait_time = QDRV_IOCTL_HARD_IRQ_WAIT_TIME;
if (ic->ic_pm_enabled)
wait_time = QDRV_IOCTL_HARD_IRQ_PM_WAIT_TIME;
} else if (in_isr) {
wait_time = QDRV_IOCTL_ISR_WAIT_TIME;
} else {
wait_time = QDRV_IOCTL_PROC_WAIT_TIME;
}
DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_HLINK,
"HLINK MSG: %d Dev %d args %08X %08X called in %s context\n",
ioctl->ioctl_command, ioctl->ioctl_dev, ioctl->ioctl_arg1, ioctl->ioctl_arg2,
in_isr ? "interrupt" : "process");
if (mac->dead) {
static int count_msg = 0;
#define QDRV_MAX_DEAD_MSG 25
if ((count_msg++) <= QDRV_MAX_DEAD_MSG) {
DBGPRINTF_E("(%d)Dropping IOCTL %p due to dead MAC: %d dev %d args %08X %08X called in %s context\n",
count_msg, ioctl, ioctl->ioctl_command, ioctl->ioctl_dev, ioctl->ioctl_arg1,
ioctl->ioctl_arg2, in_isr ? "interrupt" : "process");
if (count_msg == QDRV_MAX_DEAD_MSG) {
DBGPRINTF_E("Restricting dead IOCTL messages\n");
}
}
rc = 1;
qdrv_free_ioctl(ioctl);
return (rc);
}
/* IOCTLs can be sent from non-sleep context. So we are forced to busy
* wait. MuC treats IOCTL msgs as highest prio task
*/
desc = "waiting for empty mbox";
QDRV_IOCTL_EVNT_TIMEOUT(*mbox, wait_time, desc, rc, !in_isr);
if (rc) {
goto hlink_timeout;
}
/* We should check again if mbx empty. NON preempt kernel we should be ok */
spin_lock_irqsave(&qw->flowlock, flags);
/*
* Push msg into mbx
* - the current msg is offset into the dma region by ioctl index
*/
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE | QDRV_LF_HLINK,
"MBOX %p\n", mbox);
*mbox = (u32)((ioctl - qw->tx_if.hl_ring) + (struct host_ioctl *)qw->tx_if.hl_ring_dma);
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE | QDRV_LF_HLINK,
"set MBOX %p\n", mbox);
spin_unlock_irqrestore(&qw->flowlock, flags);
/* Interrupt Muc */
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE | QDRV_LF_HLINK,
"Interrupting MuC %p\n", qw->mac);
qdrv_mac_interrupt_muc(qw->mac);
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_TRACE | QDRV_LF_HLINK,
"Interrupted MuC %p\n", qw->mac);
desc = "waiting for MuC dequeue";
QDRV_IOCTL_EVNT_TIMEOUT(*mbox, wait_time, desc, rc, !in_isr);
if (rc) {
goto hlink_timeout;
}
desc = "waiting for ioctl completion";
// when in calibration mode, it takes long time. So, increase time-out period
if (soc_shared_params->calstate == QTN_CALSTATE_DEFAULT) {
QDRV_IOCTL_EVNT_TIMEOUT(
!(ioctl->ioctl_rc & (QTN_HLINK_RC_DONE | QTN_HLINK_RC_ERR)),
wait_time, desc, rc, !in_isr);
} else {
QDRV_IOCTL_EVNT_TIMEOUT(
!(ioctl->ioctl_rc & (QTN_HLINK_RC_DONE | QTN_HLINK_RC_ERR)),
wait_time*100, desc, rc, !in_isr);
}
if (rc) {
goto hlink_timeout;
}
rc = ioctl->ioctl_rc;
qdrv_free_ioctl(ioctl);
mac->ioctl_fail_count = 0;
return rc;
hlink_timeout:
DBGPRINTF_E("HLINK MSG timed out while %s: cmd=%d dev=%d args=%08x %08x status=%u rc=%u ctxt=%s\n",
desc,
ioctl->ioctl_command, ioctl->ioctl_dev, ioctl->ioctl_arg1, ioctl->ioctl_arg2,
ioctl->ioctl_status, ioctl->ioctl_rc,
in_isr ? "interrupt" : "process");
rc = QDRV_IOCTL_RET_TIMEOUT;
qdrv_free_ioctl(ioctl);
/* If too many failed IOCTLs, perform some system action (eg, panic, gather logs, whatever). */
mac->ioctl_fail_count++;
#define QDRV_MAX_IOCTL_FAIL_DIE 16
if (mac->ioctl_fail_count > QDRV_MAX_IOCTL_FAIL_DIE) {
DBGPRINTF_E("Too many failed IOCTLs (%d) - MAC is dead\n", mac->ioctl_fail_count);
qdrv_mac_die_action(mac);
}
return rc;
#endif /* #ifdef MTEST */
}
void* qdrv_hostlink_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag)
{
void *ret = dma_alloc_coherent(dev, size, dma_handle, flag);
if(dma_handle && *dma_handle) {
*dma_handle = (dma_addr_t)muc_to_nocache((void*)(*dma_handle));
}
return ret;
}
void qdrv_hostlink_free_coherent(struct device *dev, size_t size, void *kvaddr, dma_addr_t dma_handle)
{
/* TODO: Since we are freeing DMA memory while in softirq context, this
* operation results into warn_on from kernel.
*/
#if 1
dma_free_coherent(dev, size, kvaddr, (dma_addr_t)nocache_to_muc((void*)dma_handle));
#else
if (dma_release_from_coherent(dev, get_order(size), cpu_addr))
return;
unmap_kernel_range((unsigned long)kvaddr, PAGE_ALIGN(size));
free_pages_exact((void *)plat_dma_to_phys(dev, dma_handle),
size);
#endif
}
/* THese are the Per vap IOCLTS that finally go over the DEV ioctl */
void vnet_free_ioctl(struct host_ioctl *ioctl)
{
if (ioctl) {
qdrv_free_ioctl(ioctl);
}
}
struct host_ioctl *vnet_alloc_ioctl(struct qdrv_vap *qv)
{
struct qdrv_wlan *qw = (struct qdrv_wlan*)qv->parent;
struct host_ioctl *ioctl;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl != NULL) {
ioctl->ioctl_dev = qv->devid;
}
return (ioctl);
}
int vnet_send_ioctl(struct qdrv_vap *qv, struct host_ioctl *ioctl)
{
struct qdrv_wlan *qw = (struct qdrv_wlan*)qv->parent;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_msg_calcmd(struct qdrv_wlan *qw, int cmdlen, dma_addr_t cmd_dma)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_CALCMD;
ioctl->ioctl_arg1 = 0; /*sys_rev_num*/;
ioctl->ioctl_arg2 = cmdlen;
ioctl->ioctl_argp = cmd_dma;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_msg_cmd(struct qdrv_wlan *qw, u_int32_t cmd, u_int32_t arg)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_CMD;
ioctl->ioctl_arg1 = cmd;
ioctl->ioctl_arg2 = arg;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
static int qdrv_get_vap_id(const char *ifname, uint8_t *vap_id)
{
if (vap_id == NULL || sscanf(ifname, "wifi%hhu", vap_id) != 1)
return -EINVAL;
if (*vap_id >= QDRV_MAX_BSS_VAPS)
return -EINVAL;
return 0;
}
int qdrv_hostlink_msg_create_vap(struct qdrv_wlan *qw,
const char *name_lhost, const uint8_t *mac_addr, int devid, int opmode, int flags)
{
struct host_ioctl *ioctl;
struct qtn_vap_args *vap_args = NULL;
dma_addr_t args_dma;
int alloc_len;
int unit;
int ret;
uint8_t vap_id = QTN_MAX_BSS_VAPS;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
ret = -ENOMEM;
goto out_no_free;
}
if (opmode == IEEE80211_M_WDS)
vap_id = 0;
else {
ret = qdrv_get_vap_id(name_lhost, &vap_id);
if (ret != 0)
goto out_free_ioctl;
}
alloc_len = sizeof(*vap_args) + 1;
vap_args = qdrv_hostlink_alloc_coherent(NULL, alloc_len,
&args_dma, GFP_ATOMIC);
if (vap_args == NULL) {
DBGPRINTF_E("Failed allocate %d bytes for name\n", alloc_len);
ret = -ENOMEM;
goto out_free_ioctl;
}
ioctl->ioctl_command = IOCTL_DEV_VAPCREATE;
ioctl->ioctl_arg1 = unit | (flags << 8) | (opmode << 16);
ioctl->ioctl_arg2 = devid;
ioctl->ioctl_argp = args_dma;
memset(vap_args, 0, sizeof(*vap_args));
strncpy(vap_args->vap_name, name_lhost, sizeof(vap_args->vap_name)-1);
vap_args->vap_name[sizeof(vap_args->vap_name)-1] = '\0';
memcpy(vap_args->vap_macaddr, mac_addr, IEEE80211_ADDR_LEN);
vap_args->vap_id = vap_id;
ret = qdrv_send_ioctl(qw, ioctl);
qdrv_hostlink_free_coherent(NULL, alloc_len, vap_args, args_dma);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return ret;
out_free_ioctl:
qdrv_free_ioctl(ioctl);
out_no_free:
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return ret;
}
int qdrv_hostlink_msg_delete_vap(struct qdrv_wlan *qw, struct net_device *vdev)
{
struct host_ioctl *ioctl;
int unit;
int devid;
struct qdrv_vap *qv = netdev_priv(vdev);
unit = qw->unit;
devid = qv->devid;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_VAPDELETE;
ioctl->ioctl_arg1 = unit;
ioctl->ioctl_arg2 = devid;
ioctl->ioctl_argp = 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_sample_chan_cancel(struct qdrv_wlan *qw, struct qtn_samp_chan_info *samp_chan_bus)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_SAMPLE_CHANNEL_CANCEL;
ioctl->ioctl_arg1 = (u32)samp_chan_bus;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_sample_chan(struct qdrv_wlan *qw, struct qtn_samp_chan_info *samp_chan_bus)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_SAMPLE_CHANNEL;
ioctl->ioctl_arg1 = (u32)samp_chan_bus;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_remain_chan(struct qdrv_wlan *qw, struct qtn_remain_chan_info *remain_chan_bus)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_REMAIN_CHANNEL;
ioctl->ioctl_arg1 = (uint32_t)remain_chan_bus;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_suspend_off_chan(struct qdrv_wlan *qw, uint32_t suspend)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_SUSPEND_OFF_CHANNEL;
ioctl->ioctl_arg1 = suspend;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_set_ocac(struct qdrv_wlan *qw, struct qtn_ocac_info *ocac_bus)
{
struct host_ioctl *ioctl;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_SET_OCAC;
ioctl->ioctl_arg1 = (u32)ocac_bus;
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_meas_chan(struct qdrv_wlan *qw, struct qtn_meas_chan_info *meas_chan_bus)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_MEAS_CHANNEL;
ioctl->ioctl_arg1 = (u32)meas_chan_bus;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_rxgain_params(struct qdrv_wlan *qw, uint32_t index, struct qtn_rf_rxgain_params *rx_gain_params)
{
struct host_ioctl *ioctl;
dma_addr_t args_dma = 0;
struct qtn_rf_rxgain_params *sp_rxgain_params=NULL;
int alloc_len = sizeof(*sp_rxgain_params) + 1;
int ret = 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
if (rx_gain_params != NULL) {
sp_rxgain_params = qdrv_hostlink_alloc_coherent(NULL, alloc_len,
&args_dma, GFP_ATOMIC);
if (sp_rxgain_params == NULL) {
qdrv_free_ioctl(ioctl);
DBGPRINTF_E("Failed allocate %d bytes for name\n", alloc_len);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
*sp_rxgain_params = *rx_gain_params;
}
ioctl->ioctl_command = IOCTL_DEV_SET_RX_GAIN_PARAMS;
ioctl->ioctl_arg1 = index;
ioctl->ioctl_arg2 = args_dma;
ret = qdrv_send_ioctl(qw, ioctl);
if (sp_rxgain_params != NULL) {
qdrv_hostlink_free_coherent(NULL, alloc_len, sp_rxgain_params, args_dma);
}
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return ret;
}
#ifdef QTN_BG_SCAN
int qdrv_hostlink_bgscan_chan(struct qdrv_wlan *qw, struct qtn_scan_chan_info *scan_chan_bus)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_BGSCAN_CHANNEL;
ioctl->ioctl_arg1 = (u32)scan_chan_bus;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
#endif /* QTN_BG_SCAN */
int qdrv_hostlink_store_txpow(struct qdrv_wlan *qw, u_int32_t txpow)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
#if 0
DPRINTF(LL_1, LF_ERROR, (DBGEFMT "ioctl NULL\n", DBGARG));
DPRINTF(LL_1, LF_TRACE, (DBGFMT "<--Exit\n", DBGARG));
#endif
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_STORE_TXPOW;
ioctl->ioctl_arg1 = qw->rf_chipid;
ioctl->ioctl_arg2 = txpow;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_setchan(struct qdrv_wlan *qw, uint32_t freq_band, uint32_t qtn_chan)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_CHANGE_CHANNEL;
ioctl->ioctl_arg1 = freq_band;
ioctl->ioctl_arg2 = qtn_chan;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_setchan_deferred(struct qdrv_wlan *qw, struct qtn_csa_info *csa_phyaddr_info)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_CHANGE_CHAN_DEFERRED;
ioctl->ioctl_arg1 = (u32)csa_phyaddr_info;
DBGPRINTF(DBG_LL_CRIT, QDRV_LF_HLINK,
"sending to %p muc\n", csa_phyaddr_info);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_xmitctl(struct qdrv_wlan *qw, bool enable_xmit)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_XMITCTL;
ioctl->ioctl_arg1 = (enable_xmit)? 1 : 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_use_rtscts(struct qdrv_wlan *qw, int rtscts_required)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_USE_RTS_CTS;
ioctl->ioctl_arg1 = (rtscts_required)? 1 : 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
#ifdef QDRV_FEATURE_KILL_MUC
int qdrv_hostlink_killmuc(struct qdrv_wlan *qw)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
ioctl->ioctl_command = IOCTL_DEV_KILL_MUC;
ioctl->ioctl_arg1 = unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
#endif
#ifdef CONFIG_QVSP
int qdrv_hostlink_qvsp(struct qdrv_wlan *qw, uint32_t param, uint32_t value)
{
struct host_ioctl *ioctl;
int rc;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
ioctl->ioctl_command = IOCTL_DEV_VSP;
ioctl->ioctl_arg1 = param;
ioctl->ioctl_arg2 = value;
rc = qdrv_send_ioctl(qw, ioctl);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return rc;
}
#endif
int qdrv_dump_log(struct qdrv_wlan *qw)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
ioctl->ioctl_command = IOCTL_DEV_DUMP_LOG;
ioctl->ioctl_arg1 = unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_msg_set_wifi_macaddr( struct qdrv_wlan *qw, u8 *new_macaddr )
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
ioctl->ioctl_command = IOCTL_DEV_SET_MACADDR;
ioctl->ioctl_arg1 = ((new_macaddr[ 0 ] << 24) | (new_macaddr[ 1 ] << 16) | (new_macaddr[ 2 ] << 8) | new_macaddr[ 3 ]);
ioctl->ioctl_arg2 = ((new_macaddr[ 4 ] << 8) | new_macaddr[ 5 ]);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_setscanmode(struct qdrv_wlan *qw, u_int32_t scanmode)
{
struct host_ioctl *ioctl;
struct ieee80211com *ic = &qw->ic;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
if ((TAILQ_FIRST(&ic->ic_vaps))->iv_opmode == IEEE80211_M_STA) {
ioctl->ioctl_command = IOCTL_DEV_SET_SCANMODE_STA;
} else {
ioctl->ioctl_command = IOCTL_DEV_SET_SCANMODE;
}
ioctl->ioctl_arg1 = scanmode;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_set_hrflags(struct qdrv_wlan *qw, u_int32_t hrflags)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
ioctl->ioctl_command = IOCTL_DEV_SET_HRFLAGS;
ioctl->ioctl_arg1 = hrflags;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_power_save(struct qdrv_wlan *qw, int param, int val)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E( "ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;;
}
ioctl->ioctl_command = IOCTL_DEV_SET_POWER_SAVE;
ioctl->ioctl_arg1 = param;
ioctl->ioctl_arg2 = val;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_tx_airtime_control(struct qdrv_wlan *qw, uint32_t value)
{
struct host_ioctl *ioctl;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (!ioctl) {
DBGPRINTF_E("ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_AIRTIME_CONTROL;
ioctl->ioctl_arg1 = value;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_mu_group_update(struct qdrv_wlan *qw, struct qtn_mu_group_update_args *args)
{
struct host_ioctl *ioctl;
struct qtn_mu_group_update_args *sp_args = NULL;
dma_addr_t args_dma = 0;
int alloc_len = sizeof(struct qtn_mu_group_update_args);
int ret = 0;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
ioctl = qdrv_alloc_ioctl(qw);
if (!ioctl) {
DBGPRINTF_E("ioctl NULL\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
sp_args = qdrv_hostlink_alloc_coherent(NULL, alloc_len,
&args_dma, GFP_ATOMIC);
if (sp_args == NULL) {
qdrv_free_ioctl(ioctl);
DBGPRINTF_E("Failed to allocate %d bytes for name\n", alloc_len);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -ENOMEM;
}
*sp_args = *args;
ioctl->ioctl_command = IOCTL_DEV_MU_GROUP_UPDATE;
ioctl->ioctl_arg1 = 0;
ioctl->ioctl_arg2 = 0;
ioctl->ioctl_argp = args_dma;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
ret = qdrv_send_ioctl(qw, ioctl);
qdrv_hostlink_free_coherent(NULL, alloc_len, sp_args, args_dma);
return ret;
}
int qdrv_hostlink_send_ioctl_args(struct qdrv_wlan *qw, uint32_t command,
uint32_t arg1, uint32_t arg2)
{
struct host_ioctl *ioctl;
int unit;
unit = qw->unit;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E("ioctl %u qdrv_alloc_ioctl return NULL\n", command);
return -ENOMEM;
}
ioctl->ioctl_command = command;
ioctl->ioctl_arg1 = arg1;
ioctl->ioctl_arg2 = arg2;
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_start(struct qdrv_mac *mac)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}
int qdrv_hostlink_stop(struct qdrv_mac *mac)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}
int qdrv_hostlink_init(struct qdrv_wlan *qw, struct host_ioctl_hifinfo *hifinfo)
{
int indx;
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Allocate the Hostlink circular buffer */
qw->tx_if.hl_ring = qdrv_hostlink_alloc_coherent(NULL, QNET_HLRING_SIZE,
(dma_addr_t *) &qw->tx_if.hl_ring_dma, GFP_ATOMIC);
if(!qw->tx_if.hl_ring)
{
DBGPRINTF_E("Failed to allocate DMA memory\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(-ENOMEM);
}
memset(qw->tx_if.hl_ring, 0, QNET_HLRING_SIZE);
for(indx=0;indx<QNET_HLRING_ENTRIES;indx++){
qw->tx_if.hl_ring[indx].ioctl_status = QTN_HLINK_STATUS_AVAIL;
}
qw->tx_if.hl_read = qw->tx_if.hl_write = qw->tx_if.hl_tosend = 0;
qw->tx_if.hl_first = qw->tx_if.hl_last = NULL;
spin_lock_init(&qw->tx_if.hl_flowlock);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}
int qdrv_hostlink_exit(struct qdrv_wlan *qw)
{
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n");
/* Make sure work queues are done */
flush_scheduled_work();
dma_free_coherent(NULL, QNET_HLRING_SIZE, qw->tx_if.hl_ring,
qw->tx_if.hl_ring_dma);
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return(0);
}
int qdrv_hostlink_vlan_enable(struct qdrv_wlan *qw, int enable)
{
struct host_ioctl *ioctl;
int ret = 0;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E("ioctl cmd [%d] NULL\n", IOCTL_DEV_ENABLE_VLAN);
return -ENOMEM;
}
ioctl->ioctl_command = IOCTL_DEV_ENABLE_VLAN;
ioctl->ioctl_arg1 = enable;
ioctl->ioctl_arg2 = 0;
ioctl->ioctl_argp = 0;
ret = qdrv_send_ioctl(qw, ioctl);
return ret;
}
int qdrv_hostlink_enable_flush_data(struct qdrv_wlan *qw, int enable)
{
struct host_ioctl *ioctl;
int ret = 0;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E("ioctl cmd[%d] NULL\n", IOCTL_DEV_FLUSH_DATA);
return -ENOMEM;
}
if (enable) {
ioctl->ioctl_command = IOCTL_DEV_FLUSH_DATA;
ioctl->ioctl_arg1 = enable;
ioctl->ioctl_arg2 = 0;
ioctl->ioctl_argp = 0;
ret = qdrv_send_ioctl(qw, ioctl);
}
return ret;
}
int qdrv_hostlink_update_ocac_state_ie(struct qdrv_wlan *qw, uint8_t state, uint8_t param)
{
struct host_ioctl *ioctl;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E("%s: ioctl NULL\n", __func__);
return -1;
}
ioctl->ioctl_command = IOCTL_DEV_UPDATE_OCAC_STATE_IE;
ioctl->ioctl_arg1 = state;
ioctl->ioctl_arg2 = param;
return qdrv_send_ioctl(qw, ioctl);
}
int qdrv_hostlink_change_bcn_scheme(struct qdrv_vap *qv, int param, int value)
{
struct host_ioctl *ioctl;
struct qtn_setparams_args *args = NULL;
dma_addr_t args_dma = 0;
int ret = 0;
struct qdrv_wlan *qw = (struct qdrv_wlan*)qv->parent;
ioctl = qdrv_alloc_ioctl(qw);
if (ioctl == NULL) {
DBGPRINTF_E("ioctl cmd[%d] NULL\n", IOCTL_DEV_SETPARAMS);
return -1;
}
args = qdrv_hostlink_alloc_coherent(NULL, sizeof(*args),
&args_dma, GFP_DMA | GFP_ATOMIC);
if (args == NULL) {
qdrv_free_ioctl(ioctl);
DBGPRINTF_E("Failed allocate memory for bytes for args\n");
DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n");
return -1;
}
args->ni_param = param;
args->ni_value = value;
args->ni_len = 0;
ioctl->ioctl_command = IOCTL_DEV_SETPARAMS;
ioctl->ioctl_dev = qv->devid;
ioctl->ioctl_argp = args_dma;
ret = qdrv_send_ioctl(qw, ioctl);
qdrv_hostlink_free_coherent(NULL, sizeof(*args), args, args_dma);
return ret;
}