The first pull request for 4.13. We have a new driver qtnfmac, but also rsi driver got a support for new firmware and supporting ath10k SDIO devices was started. Major changes: ath10k * add initial SDIO support (still work in progress) rsi * new loading for the new firmware version rtlwifi * final patches for the new btcoex support rt2x00 * add device ID for Epson WN7512BEP qtnfmac * new driver for Quantenna QSR10G chipsets -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJZPpCkAAoJEG4XJFUm622bUTMH/ipenw7p7otrhw9qtSBcqBRJ hS+1i2J+VXDXptFng0ziPWVv6mvhANvBszuOmiNMOgqjx2HrVknwlKUKViklHN3p E1PE3/A3bRpzBjMk4j8r/Z7VJK3rDa4WSi/AMJtqV9noNm5FfiOrCk7rXm2MLBns x1Wyr/7OX12hiB4SCoOuOZqS/TvHlNCW71BoyRruq01N2MA1iSonLCYJbovgi7Ds 9acAY90PF8jLC+o7wxYkwuRqWncNhnKOsVNhc/6DKH91zB+C5sw2NGfD3WMfot/Z 9uC5nBWNadoLGi636y+9evIRgNAFCczCZZSUeY7jMWiVn7XyFy8zoc4fv2ZSBto= =6md1 -----END PGP SIGNATURE----- Merge tag 'wireless-drivers-next-for-davem-2017-06-12' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next Kalle Valo says: ==================== wireless-drivers-next patches for 4.13 The first pull request for 4.13. We have a new driver qtnfmac, but also rsi driver got a support for new firmware and supporting ath10k SDIO devices was started. Major changes: ath10k * add initial SDIO support (still work in progress) rsi * new loading for the new firmware version rtlwifi * final patches for the new btcoex support rt2x00 * add device ID for Epson WN7512BEP qtnfmac * new driver for Quantenna QSR10G chipsets ==================== Signed-off-by: David S. Miller <davem@davemloft.net>tirimbino
commit
63a2f310d0
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,229 @@ |
||||
/*
|
||||
* Copyright (c) 2004-2011 Atheros Communications Inc. |
||||
* Copyright (c) 2011-2012 Qualcomm Atheros, Inc. |
||||
* Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com> |
||||
* |
||||
* Permission to use, copy, modify, and/or distribute this software for any |
||||
* purpose with or without fee is hereby granted, provided that the above |
||||
* copyright notice and this permission notice appear in all copies. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
*/ |
||||
|
||||
#ifndef _SDIO_H_ |
||||
#define _SDIO_H_ |
||||
|
||||
#define ATH10K_HIF_MBOX_BLOCK_SIZE 256 |
||||
|
||||
#define QCA_MANUFACTURER_ID_BASE GENMASK(11, 8) |
||||
#define QCA_MANUFACTURER_ID_AR6005_BASE 0x5 |
||||
#define QCA_MANUFACTURER_ID_QCA9377_BASE 0x7 |
||||
#define QCA_SDIO_ID_AR6005_BASE 0x500 |
||||
#define QCA_SDIO_ID_QCA9377_BASE 0x700 |
||||
#define QCA_MANUFACTURER_ID_REV_MASK 0x00FF |
||||
#define QCA_MANUFACTURER_CODE 0x271 /* Qualcomm/Atheros */ |
||||
|
||||
#define ATH10K_SDIO_MAX_BUFFER_SIZE 4096 /*Unsure of this constant*/ |
||||
|
||||
/* Mailbox address in SDIO address space */ |
||||
#define ATH10K_HIF_MBOX_BASE_ADDR 0x1000 |
||||
#define ATH10K_HIF_MBOX_WIDTH 0x800 |
||||
|
||||
#define ATH10K_HIF_MBOX_TOT_WIDTH \ |
||||
(ATH10K_HIF_MBOX_NUM_MAX * ATH10K_HIF_MBOX_WIDTH) |
||||
|
||||
#define ATH10K_HIF_MBOX0_EXT_BASE_ADDR 0x5000 |
||||
#define ATH10K_HIF_MBOX0_EXT_WIDTH (36 * 1024) |
||||
#define ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0 (56 * 1024) |
||||
#define ATH10K_HIF_MBOX1_EXT_WIDTH (36 * 1024) |
||||
#define ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE (2 * 1024) |
||||
|
||||
#define ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH \ |
||||
(ATH10K_SDIO_MAX_BUFFER_SIZE - sizeof(struct ath10k_htc_hdr)) |
||||
|
||||
#define ATH10K_HIF_MBOX_NUM_MAX 4 |
||||
#define ATH10K_SDIO_BUS_REQUEST_MAX_NUM 64 |
||||
|
||||
#define ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ (100 * HZ) |
||||
|
||||
/* HTC runs over mailbox 0 */ |
||||
#define ATH10K_HTC_MAILBOX 0 |
||||
#define ATH10K_HTC_MAILBOX_MASK BIT(ATH10K_HTC_MAILBOX) |
||||
|
||||
/* GMBOX addresses */ |
||||
#define ATH10K_HIF_GMBOX_BASE_ADDR 0x7000 |
||||
#define ATH10K_HIF_GMBOX_WIDTH 0x4000 |
||||
|
||||
/* Modified versions of the sdio.h macros.
|
||||
* The macros in sdio.h can't be used easily with the FIELD_{PREP|GET} |
||||
* macros in bitfield.h, so we define our own macros here. |
||||
*/ |
||||
#define ATH10K_SDIO_DRIVE_DTSX_MASK \ |
||||
(SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT) |
||||
|
||||
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_B 0 |
||||
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_A 1 |
||||
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_C 2 |
||||
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_D 3 |
||||
|
||||
/* SDIO CCCR register definitions */ |
||||
#define CCCR_SDIO_IRQ_MODE_REG 0xF0 |
||||
#define CCCR_SDIO_IRQ_MODE_REG_SDIO3 0x16 |
||||
|
||||
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR 0xF2 |
||||
|
||||
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A 0x02 |
||||
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C 0x04 |
||||
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D 0x08 |
||||
|
||||
#define CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS 0xF0 |
||||
#define CCCR_SDIO_ASYNC_INT_DELAY_MASK 0xC0 |
||||
|
||||
/* mode to enable special 4-bit interrupt assertion without clock */ |
||||
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ BIT(0) |
||||
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3 BIT(1) |
||||
|
||||
#define ATH10K_SDIO_TARGET_DEBUG_INTR_MASK 0x01 |
||||
|
||||
/* The theoretical maximum number of RX messages that can be fetched
|
||||
* from the mbox interrupt handler in one loop is derived in the following |
||||
* way: |
||||
* |
||||
* Let's assume that each packet in a bundle of the maximum bundle size |
||||
* (HTC_HOST_MAX_MSG_PER_BUNDLE) has the HTC header bundle count set |
||||
* to the maximum value (HTC_HOST_MAX_MSG_PER_BUNDLE). |
||||
* |
||||
* in this case the driver must allocate |
||||
* (HTC_HOST_MAX_MSG_PER_BUNDLE * HTC_HOST_MAX_MSG_PER_BUNDLE) skb's. |
||||
*/ |
||||
#define ATH10K_SDIO_MAX_RX_MSGS \ |
||||
(HTC_HOST_MAX_MSG_PER_BUNDLE * HTC_HOST_MAX_MSG_PER_BUNDLE) |
||||
|
||||
#define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL 0x00000868u |
||||
#define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF 0xFFFEFFFF |
||||
#define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_ON 0x10000 |
||||
|
||||
struct ath10k_sdio_bus_request { |
||||
struct list_head list; |
||||
|
||||
/* sdio address */ |
||||
u32 address; |
||||
|
||||
struct sk_buff *skb; |
||||
enum ath10k_htc_ep_id eid; |
||||
int status; |
||||
/* Specifies if the current request is an HTC message.
|
||||
* If not, the eid is not applicable an the TX completion handler |
||||
* associated with the endpoint will not be invoked. |
||||
*/ |
||||
bool htc_msg; |
||||
/* Completion that (if set) will be invoked for non HTC requests
|
||||
* (htc_msg == false) when the request has been processed. |
||||
*/ |
||||
struct completion *comp; |
||||
}; |
||||
|
||||
struct ath10k_sdio_rx_data { |
||||
struct sk_buff *skb; |
||||
size_t alloc_len; |
||||
size_t act_len; |
||||
enum ath10k_htc_ep_id eid; |
||||
bool part_of_bundle; |
||||
bool last_in_bundle; |
||||
bool trailer_only; |
||||
int status; |
||||
}; |
||||
|
||||
struct ath10k_sdio_irq_proc_regs { |
||||
u8 host_int_status; |
||||
u8 cpu_int_status; |
||||
u8 error_int_status; |
||||
u8 counter_int_status; |
||||
u8 mbox_frame; |
||||
u8 rx_lookahead_valid; |
||||
u8 host_int_status2; |
||||
u8 gmbox_rx_avail; |
||||
__le32 rx_lookahead[2]; |
||||
__le32 rx_gmbox_lookahead_alias[2]; |
||||
}; |
||||
|
||||
struct ath10k_sdio_irq_enable_regs { |
||||
u8 int_status_en; |
||||
u8 cpu_int_status_en; |
||||
u8 err_int_status_en; |
||||
u8 cntr_int_status_en; |
||||
}; |
||||
|
||||
struct ath10k_sdio_irq_data { |
||||
/* protects irq_proc_reg and irq_en_reg below.
|
||||
* We use a mutex here and not a spinlock since we will have the |
||||
* mutex locked while calling the sdio_memcpy_ functions. |
||||
* These function require non atomic context, and hence, spinlocks |
||||
* can be held while calling these functions. |
||||
*/ |
||||
struct mutex mtx; |
||||
struct ath10k_sdio_irq_proc_regs *irq_proc_reg; |
||||
struct ath10k_sdio_irq_enable_regs *irq_en_reg; |
||||
}; |
||||
|
||||
struct ath10k_mbox_ext_info { |
||||
u32 htc_ext_addr; |
||||
u32 htc_ext_sz; |
||||
}; |
||||
|
||||
struct ath10k_mbox_info { |
||||
u32 htc_addr; |
||||
struct ath10k_mbox_ext_info ext_info[2]; |
||||
u32 block_size; |
||||
u32 block_mask; |
||||
u32 gmbox_addr; |
||||
u32 gmbox_sz; |
||||
}; |
||||
|
||||
struct ath10k_sdio { |
||||
struct sdio_func *func; |
||||
|
||||
struct ath10k_mbox_info mbox_info; |
||||
bool swap_mbox; |
||||
u32 mbox_addr[ATH10K_HTC_EP_COUNT]; |
||||
u32 mbox_size[ATH10K_HTC_EP_COUNT]; |
||||
|
||||
/* available bus requests */ |
||||
struct ath10k_sdio_bus_request bus_req[ATH10K_SDIO_BUS_REQUEST_MAX_NUM]; |
||||
/* free list of bus requests */ |
||||
struct list_head bus_req_freeq; |
||||
/* protects access to bus_req_freeq */ |
||||
spinlock_t lock; |
||||
|
||||
struct ath10k_sdio_rx_data rx_pkts[ATH10K_SDIO_MAX_RX_MSGS]; |
||||
size_t n_rx_pkts; |
||||
|
||||
struct ath10k *ar; |
||||
struct ath10k_sdio_irq_data irq_data; |
||||
|
||||
/* temporary buffer for BMI requests */ |
||||
u8 *bmi_buf; |
||||
|
||||
wait_queue_head_t irq_wq; |
||||
|
||||
bool is_disabled; |
||||
|
||||
struct workqueue_struct *workqueue; |
||||
struct work_struct wr_async_work; |
||||
struct list_head wr_asyncq; |
||||
/* protects access to wr_asyncq */ |
||||
spinlock_t wr_async_lock; |
||||
}; |
||||
|
||||
static inline struct ath10k_sdio *ath10k_sdio_priv(struct ath10k *ar) |
||||
{ |
||||
return (struct ath10k_sdio *)ar->drv_priv; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,16 @@ |
||||
config WLAN_VENDOR_QUANTENNA |
||||
bool "Quantenna wireless cards support" |
||||
default y |
||||
---help--- |
||||
If you have a wireless card belonging to this class, say Y. |
||||
|
||||
Note that the answer to this question doesn't directly affect the |
||||
kernel: saying N will just cause the configurator to skip all |
||||
the questions about cards. If you say Y, you will be asked for |
||||
your specific card in the following questions. |
||||
|
||||
if WLAN_VENDOR_QUANTENNA |
||||
|
||||
source "drivers/net/wireless/quantenna/qtnfmac/Kconfig" |
||||
|
||||
endif # WLAN_VENDOR_QUANTENNA |
@ -0,0 +1,6 @@ |
||||
#
|
||||
# Copyright (c) 2015-2016 Quantenna Communications, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_QTNFMAC) += qtnfmac/
|
@ -0,0 +1,19 @@ |
||||
config QTNFMAC |
||||
tristate |
||||
depends on QTNFMAC_PEARL_PCIE |
||||
default m if QTNFMAC_PEARL_PCIE=m |
||||
default y if QTNFMAC_PEARL_PCIE=y |
||||
|
||||
config QTNFMAC_PEARL_PCIE |
||||
tristate "Quantenna QSR10g PCIe support" |
||||
default n |
||||
depends on HAS_DMA && PCI && CFG80211 |
||||
select QTNFMAC |
||||
select FW_LOADER |
||||
select CRC32 |
||||
---help--- |
||||
This option adds support for wireless adapters based on Quantenna |
||||
802.11ac QSR10g (aka Pearl) FullMAC chipset running over PCIe. |
||||
|
||||
If you choose to build it as a module, two modules will be built: |
||||
qtnfmac.ko and qtnfmac_pearl_pcie.ko. |
@ -0,0 +1,31 @@ |
||||
#
|
||||
# Copyright (c) 2015-2016 Quantenna Communications, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
ccflags-y += \
|
||||
-Idrivers/net/wireless/quantenna/qtnfmac
|
||||
|
||||
obj-$(CONFIG_QTNFMAC) += qtnfmac.o
|
||||
qtnfmac-objs += \
|
||||
core.o \
|
||||
commands.o \
|
||||
trans.o \
|
||||
cfg80211.o \
|
||||
event.o \
|
||||
util.o \
|
||||
qlink_util.o
|
||||
|
||||
#
|
||||
|
||||
obj-$(CONFIG_QTNFMAC_PEARL_PCIE) += qtnfmac_pearl_pcie.o
|
||||
|
||||
qtnfmac_pearl_pcie-objs += \
|
||||
shm_ipc.o \
|
||||
pearl/pcie.o
|
||||
|
||||
qtnfmac_pearl_pcie-$(CONFIG_DEBUG_FS) += debug.o
|
||||
|
||||
#
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN
|
@ -0,0 +1,139 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Quantenna Communications |
||||
* |
||||
* Permission to use, copy, modify, and/or distribute this software for any |
||||
* purpose with or without fee is hereby granted, provided that the above |
||||
* copyright notice and this permission notice appear in all copies. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
*/ |
||||
|
||||
#ifndef QTNFMAC_BUS_H |
||||
#define QTNFMAC_BUS_H |
||||
|
||||
#include <linux/netdevice.h> |
||||
#include <linux/workqueue.h> |
||||
|
||||
#define QTNF_MAX_MAC 3 |
||||
|
||||
enum qtnf_fw_state { |
||||
QTNF_FW_STATE_RESET, |
||||
QTNF_FW_STATE_FW_DNLD_DONE, |
||||
QTNF_FW_STATE_BOOT_DONE, |
||||
QTNF_FW_STATE_ACTIVE, |
||||
QTNF_FW_STATE_DEAD, |
||||
}; |
||||
|
||||
struct qtnf_bus; |
||||
|
||||
struct qtnf_bus_ops { |
||||
/* mgmt methods */ |
||||
int (*preinit)(struct qtnf_bus *); |
||||
void (*stop)(struct qtnf_bus *); |
||||
|
||||
/* control path methods */ |
||||
int (*control_tx)(struct qtnf_bus *, struct sk_buff *); |
||||
|
||||
/* data xfer methods */ |
||||
int (*data_tx)(struct qtnf_bus *, struct sk_buff *); |
||||
void (*data_tx_timeout)(struct qtnf_bus *, struct net_device *); |
||||
void (*data_rx_start)(struct qtnf_bus *); |
||||
void (*data_rx_stop)(struct qtnf_bus *); |
||||
}; |
||||
|
||||
struct qtnf_bus { |
||||
struct device *dev; |
||||
enum qtnf_fw_state fw_state; |
||||
u32 chip; |
||||
u32 chiprev; |
||||
const struct qtnf_bus_ops *bus_ops; |
||||
struct qtnf_wmac *mac[QTNF_MAX_MAC]; |
||||
struct qtnf_qlink_transport trans; |
||||
struct qtnf_hw_info hw_info; |
||||
char fwname[32]; |
||||
struct napi_struct mux_napi; |
||||
struct net_device mux_dev; |
||||
struct completion request_firmware_complete; |
||||
struct workqueue_struct *workqueue; |
||||
struct work_struct event_work; |
||||
struct mutex bus_lock; /* lock during command/event processing */ |
||||
struct dentry *dbg_dir; |
||||
/* bus private data */ |
||||
char bus_priv[0] __aligned(sizeof(void *)); |
||||
}; |
||||
|
||||
static inline void *get_bus_priv(struct qtnf_bus *bus) |
||||
{ |
||||
if (WARN(!bus, "qtnfmac: invalid bus pointer")) |
||||
return NULL; |
||||
|
||||
return &bus->bus_priv; |
||||
} |
||||
|
||||
/* callback wrappers */ |
||||
|
||||
static inline int qtnf_bus_preinit(struct qtnf_bus *bus) |
||||
{ |
||||
if (!bus->bus_ops->preinit) |
||||
return 0; |
||||
return bus->bus_ops->preinit(bus); |
||||
} |
||||
|
||||
static inline void qtnf_bus_stop(struct qtnf_bus *bus) |
||||
{ |
||||
if (!bus->bus_ops->stop) |
||||
return; |
||||
bus->bus_ops->stop(bus); |
||||
} |
||||
|
||||
static inline int qtnf_bus_data_tx(struct qtnf_bus *bus, struct sk_buff *skb) |
||||
{ |
||||
return bus->bus_ops->data_tx(bus, skb); |
||||
} |
||||
|
||||
static inline void |
||||
qtnf_bus_data_tx_timeout(struct qtnf_bus *bus, struct net_device *ndev) |
||||
{ |
||||
return bus->bus_ops->data_tx_timeout(bus, ndev); |
||||
} |
||||
|
||||
static inline int qtnf_bus_control_tx(struct qtnf_bus *bus, struct sk_buff *skb) |
||||
{ |
||||
return bus->bus_ops->control_tx(bus, skb); |
||||
} |
||||
|
||||
static inline void qtnf_bus_data_rx_start(struct qtnf_bus *bus) |
||||
{ |
||||
return bus->bus_ops->data_rx_start(bus); |
||||
} |
||||
|
||||
static inline void qtnf_bus_data_rx_stop(struct qtnf_bus *bus) |
||||
{ |
||||
return bus->bus_ops->data_rx_stop(bus); |
||||
} |
||||
|
||||
static __always_inline void qtnf_bus_lock(struct qtnf_bus *bus) |
||||
{ |
||||
mutex_lock(&bus->bus_lock); |
||||
} |
||||
|
||||
static __always_inline void qtnf_bus_unlock(struct qtnf_bus *bus) |
||||
{ |
||||
mutex_unlock(&bus->bus_lock); |
||||
} |
||||
|
||||
/* interface functions from common layer */ |
||||
|
||||
void qtnf_rx_frame(struct device *dev, struct sk_buff *rxp); |
||||
int qtnf_core_attach(struct qtnf_bus *bus); |
||||
void qtnf_core_detach(struct qtnf_bus *bus); |
||||
void qtnf_txflowblock(struct device *dev, bool state); |
||||
void qtnf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); |
||||
|
||||
#endif /* QTNFMAC_BUS_H */ |
@ -0,0 +1,995 @@ |
||||
/*
|
||||
* Copyright (c) 2012-2012 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/etherdevice.h> |
||||
#include <linux/vmalloc.h> |
||||
#include <linux/ieee80211.h> |
||||
#include <net/cfg80211.h> |
||||
#include <net/netlink.h> |
||||
|
||||
#include "cfg80211.h" |
||||
#include "commands.h" |
||||
#include "core.h" |
||||
#include "util.h" |
||||
#include "bus.h" |
||||
|
||||
/* Supported rates to be advertised to the cfg80211 */ |
||||
static struct ieee80211_rate qtnf_rates_2g[] = { |
||||
{.bitrate = 10, .hw_value = 2, }, |
||||
{.bitrate = 20, .hw_value = 4, }, |
||||
{.bitrate = 55, .hw_value = 11, }, |
||||
{.bitrate = 110, .hw_value = 22, }, |
||||
{.bitrate = 60, .hw_value = 12, }, |
||||
{.bitrate = 90, .hw_value = 18, }, |
||||
{.bitrate = 120, .hw_value = 24, }, |
||||
{.bitrate = 180, .hw_value = 36, }, |
||||
{.bitrate = 240, .hw_value = 48, }, |
||||
{.bitrate = 360, .hw_value = 72, }, |
||||
{.bitrate = 480, .hw_value = 96, }, |
||||
{.bitrate = 540, .hw_value = 108, }, |
||||
}; |
||||
|
||||
/* Supported rates to be advertised to the cfg80211 */ |
||||
static struct ieee80211_rate qtnf_rates_5g[] = { |
||||
{.bitrate = 60, .hw_value = 12, }, |
||||
{.bitrate = 90, .hw_value = 18, }, |
||||
{.bitrate = 120, .hw_value = 24, }, |
||||
{.bitrate = 180, .hw_value = 36, }, |
||||
{.bitrate = 240, .hw_value = 48, }, |
||||
{.bitrate = 360, .hw_value = 72, }, |
||||
{.bitrate = 480, .hw_value = 96, }, |
||||
{.bitrate = 540, .hw_value = 108, }, |
||||
}; |
||||
|
||||
/* Supported crypto cipher suits to be advertised to cfg80211 */ |
||||
static const u32 qtnf_cipher_suites[] = { |
||||
WLAN_CIPHER_SUITE_TKIP, |
||||
WLAN_CIPHER_SUITE_CCMP, |
||||
WLAN_CIPHER_SUITE_AES_CMAC, |
||||
}; |
||||
|
||||
/* Supported mgmt frame types to be advertised to cfg80211 */ |
||||
static const struct ieee80211_txrx_stypes |
||||
qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = { |
||||
[NL80211_IFTYPE_STATION] = { |
||||
.tx = BIT(IEEE80211_STYPE_ACTION >> 4), |
||||
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | |
||||
BIT(IEEE80211_STYPE_PROBE_REQ >> 4), |
||||
}, |
||||
[NL80211_IFTYPE_AP] = { |
||||
.tx = BIT(IEEE80211_STYPE_ACTION >> 4), |
||||
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | |
||||
BIT(IEEE80211_STYPE_PROBE_REQ >> 4), |
||||
}, |
||||
}; |
||||
|
||||
static int |
||||
qtnf_change_virtual_intf(struct wiphy *wiphy, |
||||
struct net_device *dev, |
||||
enum nl80211_iftype type, |
||||
struct vif_params *params) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
u8 *mac_addr; |
||||
int ret; |
||||
|
||||
if (params) |
||||
mac_addr = params->macaddr; |
||||
else |
||||
mac_addr = NULL; |
||||
|
||||
qtnf_scan_done(vif->mac, true); |
||||
|
||||
ret = qtnf_cmd_send_change_intf_type(vif, type, mac_addr); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to change VIF type: %d\n", |
||||
vif->mac->macid, vif->vifid, ret); |
||||
return ret; |
||||
} |
||||
|
||||
vif->wdev.iftype = type; |
||||
return 0; |
||||
} |
||||
|
||||
int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) |
||||
{ |
||||
struct net_device *netdev = wdev->netdev; |
||||
struct qtnf_vif *vif; |
||||
|
||||
if (WARN_ON(!netdev)) |
||||
return -EFAULT; |
||||
|
||||
vif = qtnf_netdev_get_priv(wdev->netdev); |
||||
|
||||
if (qtnf_cmd_send_del_intf(vif)) |
||||
pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid, |
||||
vif->vifid); |
||||
|
||||
/* Stop data */ |
||||
netif_tx_stop_all_queues(netdev); |
||||
if (netif_carrier_ok(netdev)) |
||||
netif_carrier_off(netdev); |
||||
|
||||
if (netdev->reg_state == NETREG_REGISTERED) |
||||
unregister_netdevice(netdev); |
||||
|
||||
vif->netdev->ieee80211_ptr = NULL; |
||||
vif->netdev = NULL; |
||||
vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; |
||||
eth_zero_addr(vif->mac_addr); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy, |
||||
const char *name, |
||||
unsigned char name_assign_t, |
||||
enum nl80211_iftype type, |
||||
struct vif_params *params) |
||||
{ |
||||
struct qtnf_wmac *mac; |
||||
struct qtnf_vif *vif; |
||||
u8 *mac_addr = NULL; |
||||
|
||||
mac = wiphy_priv(wiphy); |
||||
|
||||
if (!mac) |
||||
return ERR_PTR(-EFAULT); |
||||
|
||||
switch (type) { |
||||
case NL80211_IFTYPE_STATION: |
||||
case NL80211_IFTYPE_AP: |
||||
vif = qtnf_mac_get_free_vif(mac); |
||||
if (!vif) { |
||||
pr_err("MAC%u: no free VIF available\n", mac->macid); |
||||
return ERR_PTR(-EFAULT); |
||||
} |
||||
|
||||
eth_zero_addr(vif->mac_addr); |
||||
vif->bss_priority = QTNF_DEF_BSS_PRIORITY; |
||||
vif->wdev.wiphy = wiphy; |
||||
vif->wdev.iftype = type; |
||||
vif->sta_state = QTNF_STA_DISCONNECTED; |
||||
break; |
||||
default: |
||||
pr_err("MAC%u: unsupported IF type %d\n", mac->macid, type); |
||||
return ERR_PTR(-ENOTSUPP); |
||||
} |
||||
|
||||
if (params) |
||||
mac_addr = params->macaddr; |
||||
|
||||
if (qtnf_cmd_send_add_intf(vif, type, mac_addr)) { |
||||
pr_err("VIF%u.%u: failed to add VIF\n", mac->macid, vif->vifid); |
||||
goto err_cmd; |
||||
} |
||||
|
||||
if (!is_valid_ether_addr(vif->mac_addr)) { |
||||
pr_err("VIF%u.%u: FW reported bad MAC: %pM\n", |
||||
mac->macid, vif->vifid, vif->mac_addr); |
||||
goto err_mac; |
||||
} |
||||
|
||||
if (qtnf_core_net_attach(mac, vif, name, name_assign_t, type)) { |
||||
pr_err("VIF%u.%u: failed to attach netdev\n", mac->macid, |
||||
vif->vifid); |
||||
goto err_net; |
||||
} |
||||
|
||||
vif->wdev.netdev = vif->netdev; |
||||
return &vif->wdev; |
||||
|
||||
err_net: |
||||
vif->netdev = NULL; |
||||
err_mac: |
||||
qtnf_cmd_send_del_intf(vif); |
||||
err_cmd: |
||||
vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; |
||||
|
||||
return ERR_PTR(-EFAULT); |
||||
} |
||||
|
||||
static int qtnf_mgmt_set_appie(struct qtnf_vif *vif, |
||||
const struct cfg80211_beacon_data *info) |
||||
{ |
||||
int ret = 0; |
||||
|
||||
if (!info->beacon_ies || !info->beacon_ies_len) { |
||||
ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, |
||||
NULL, 0); |
||||
} else { |
||||
ret = qtnf_cmd_send_mgmt_set_appie(vif, QLINK_MGMT_FRAME_BEACON, |
||||
info->beacon_ies, |
||||
info->beacon_ies_len); |
||||
} |
||||
|
||||
if (ret) |
||||
goto out; |
||||
|
||||
if (!info->proberesp_ies || !info->proberesp_ies_len) { |
||||
ret = qtnf_cmd_send_mgmt_set_appie(vif, |
||||
QLINK_MGMT_FRAME_PROBE_RESP, |
||||
NULL, 0); |
||||
} else { |
||||
ret = qtnf_cmd_send_mgmt_set_appie(vif, |
||||
QLINK_MGMT_FRAME_PROBE_RESP, |
||||
info->proberesp_ies, |
||||
info->proberesp_ies_len); |
||||
} |
||||
|
||||
if (ret) |
||||
goto out; |
||||
|
||||
if (!info->assocresp_ies || !info->assocresp_ies_len) { |
||||
ret = qtnf_cmd_send_mgmt_set_appie(vif, |
||||
QLINK_MGMT_FRAME_ASSOC_RESP, |
||||
NULL, 0); |
||||
} else { |
||||
ret = qtnf_cmd_send_mgmt_set_appie(vif, |
||||
QLINK_MGMT_FRAME_ASSOC_RESP, |
||||
info->assocresp_ies, |
||||
info->assocresp_ies_len); |
||||
} |
||||
|
||||
out: |
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_change_beacon(struct wiphy *wiphy, struct net_device *dev, |
||||
struct cfg80211_beacon_data *info) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
|
||||
if (!(vif->bss_status & QTNF_STATE_AP_START)) { |
||||
pr_err("VIF%u.%u: not started\n", vif->mac->macid, vif->vifid); |
||||
return -EFAULT; |
||||
} |
||||
|
||||
return qtnf_mgmt_set_appie(vif, info); |
||||
} |
||||
|
||||
static int qtnf_start_ap(struct wiphy *wiphy, struct net_device *dev, |
||||
struct cfg80211_ap_settings *settings) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
struct qtnf_bss_config *bss_cfg; |
||||
int ret; |
||||
|
||||
bss_cfg = &vif->bss_cfg; |
||||
|
||||
memset(bss_cfg, 0, sizeof(*bss_cfg)); |
||||
|
||||
bss_cfg->bcn_period = settings->beacon_interval; |
||||
bss_cfg->dtim = settings->dtim_period; |
||||
bss_cfg->auth_type = settings->auth_type; |
||||
bss_cfg->privacy = settings->privacy; |
||||
|
||||
bss_cfg->ssid_len = settings->ssid_len; |
||||
memcpy(&bss_cfg->ssid, settings->ssid, bss_cfg->ssid_len); |
||||
|
||||
memcpy(&bss_cfg->chandef, &settings->chandef, |
||||
sizeof(struct cfg80211_chan_def)); |
||||
memcpy(&bss_cfg->crypto, &settings->crypto, |
||||
sizeof(struct cfg80211_crypto_settings)); |
||||
|
||||
ret = qtnf_cmd_send_config_ap(vif); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to push config to FW\n", |
||||
vif->mac->macid, vif->vifid); |
||||
goto out; |
||||
} |
||||
|
||||
if (!(vif->bss_status & QTNF_STATE_AP_CONFIG)) { |
||||
pr_err("VIF%u.%u: AP config failed in FW\n", vif->mac->macid, |
||||
vif->vifid); |
||||
ret = -EFAULT; |
||||
goto out; |
||||
} |
||||
|
||||
ret = qtnf_mgmt_set_appie(vif, &settings->beacon); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to add IEs to beacon\n", |
||||
vif->mac->macid, vif->vifid); |
||||
goto out; |
||||
} |
||||
|
||||
ret = qtnf_cmd_send_start_ap(vif); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to start AP\n", vif->mac->macid, |
||||
vif->vifid); |
||||
goto out; |
||||
} |
||||
|
||||
if (!(vif->bss_status & QTNF_STATE_AP_START)) { |
||||
pr_err("VIF%u.%u: FW failed to start AP operation\n", |
||||
vif->mac->macid, vif->vifid); |
||||
ret = -EFAULT; |
||||
} |
||||
|
||||
out: |
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_stop_ap(struct wiphy *wiphy, struct net_device *dev) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
ret = qtnf_cmd_send_stop_ap(vif); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to stop AP operation in FW\n", |
||||
vif->mac->macid, vif->vifid); |
||||
vif->bss_status &= ~QTNF_STATE_AP_START; |
||||
vif->bss_status &= ~QTNF_STATE_AP_CONFIG; |
||||
|
||||
netif_carrier_off(vif->netdev); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_set_wiphy_params(struct wiphy *wiphy, u32 changed) |
||||
{ |
||||
struct qtnf_wmac *mac = wiphy_priv(wiphy); |
||||
struct qtnf_vif *vif; |
||||
int ret; |
||||
|
||||
vif = qtnf_mac_get_base_vif(mac); |
||||
if (!vif) { |
||||
pr_err("MAC%u: primary VIF is not configured\n", mac->macid); |
||||
return -EFAULT; |
||||
} |
||||
|
||||
if (changed & (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) { |
||||
pr_err("MAC%u: can't modify retry params\n", mac->macid); |
||||
return -EOPNOTSUPP; |
||||
} |
||||
|
||||
ret = qtnf_cmd_send_update_phy_params(mac, changed); |
||||
if (ret) |
||||
pr_err("MAC%u: failed to update PHY params\n", mac->macid); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static void |
||||
qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, |
||||
u16 frame_type, bool reg) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); |
||||
u16 mgmt_type; |
||||
u16 new_mask; |
||||
u16 qlink_frame_type = 0; |
||||
|
||||
mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; |
||||
|
||||
if (reg) |
||||
new_mask = vif->mgmt_frames_bitmask | BIT(mgmt_type); |
||||
else |
||||
new_mask = vif->mgmt_frames_bitmask & ~BIT(mgmt_type); |
||||
|
||||
if (new_mask == vif->mgmt_frames_bitmask) |
||||
return; |
||||
|
||||
switch (frame_type & IEEE80211_FCTL_STYPE) { |
||||
case IEEE80211_STYPE_PROBE_REQ: |
||||
qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ; |
||||
break; |
||||
case IEEE80211_STYPE_ACTION: |
||||
qlink_frame_type = QLINK_MGMT_FRAME_ACTION; |
||||
break; |
||||
default: |
||||
pr_warn("VIF%u.%u: unsupported frame type: %X\n", |
||||
vif->mac->macid, vif->vifid, |
||||
(frame_type & IEEE80211_FCTL_STYPE) >> 4); |
||||
return; |
||||
} |
||||
|
||||
if (qtnf_cmd_send_register_mgmt(vif, qlink_frame_type, reg)) { |
||||
pr_warn("VIF%u.%u: failed to %sregister mgmt frame type 0x%x\n", |
||||
vif->mac->macid, vif->vifid, reg ? "" : "un", |
||||
frame_type); |
||||
return; |
||||
} |
||||
|
||||
vif->mgmt_frames_bitmask = new_mask; |
||||
pr_debug("VIF%u.%u: %sregistered mgmt frame type 0x%x\n", |
||||
vif->mac->macid, vif->vifid, reg ? "" : "un", frame_type); |
||||
} |
||||
|
||||
static int |
||||
qtnf_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, |
||||
struct cfg80211_mgmt_tx_params *params, u64 *cookie) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(wdev->netdev); |
||||
const struct ieee80211_mgmt *mgmt_frame = (void *)params->buf; |
||||
u32 short_cookie = prandom_u32(); |
||||
u16 flags = 0; |
||||
|
||||
*cookie = short_cookie; |
||||
|
||||
if (params->offchan) |
||||
flags |= QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN; |
||||
|
||||
if (params->no_cck) |
||||
flags |= QLINK_MGMT_FRAME_TX_FLAG_NO_CCK; |
||||
|
||||
if (params->dont_wait_for_ack) |
||||
flags |= QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT; |
||||
|
||||
pr_debug("%s freq:%u; FC:%.4X; DA:%pM; len:%zu; C:%.8X; FL:%.4X\n", |
||||
wdev->netdev->name, params->chan->center_freq, |
||||
le16_to_cpu(mgmt_frame->frame_control), mgmt_frame->da, |
||||
params->len, short_cookie, flags); |
||||
|
||||
return qtnf_cmd_send_mgmt_frame(vif, short_cookie, flags, |
||||
params->chan->center_freq, |
||||
params->buf, params->len); |
||||
} |
||||
|
||||
static int |
||||
qtnf_get_station(struct wiphy *wiphy, struct net_device *dev, |
||||
const u8 *mac, struct station_info *sinfo) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
|
||||
return qtnf_cmd_get_sta_info(vif, mac, sinfo); |
||||
} |
||||
|
||||
static int |
||||
qtnf_dump_station(struct wiphy *wiphy, struct net_device *dev, |
||||
int idx, u8 *mac, struct station_info *sinfo) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
const struct qtnf_sta_node *sta_node; |
||||
int ret; |
||||
|
||||
sta_node = qtnf_sta_list_lookup_index(&vif->sta_list, idx); |
||||
|
||||
if (unlikely(!sta_node)) |
||||
return -ENOENT; |
||||
|
||||
ether_addr_copy(mac, sta_node->mac_addr); |
||||
|
||||
ret = qtnf_cmd_get_sta_info(vif, sta_node->mac_addr, sinfo); |
||||
|
||||
if (unlikely(ret == -ENOENT)) { |
||||
qtnf_sta_list_del(&vif->sta_list, mac); |
||||
cfg80211_del_sta(vif->netdev, mac, GFP_KERNEL); |
||||
sinfo->filled = 0; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_add_key(struct wiphy *wiphy, struct net_device *dev, |
||||
u8 key_index, bool pairwise, const u8 *mac_addr, |
||||
struct key_params *params) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
ret = qtnf_cmd_send_add_key(vif, key_index, pairwise, mac_addr, params); |
||||
if (ret) |
||||
pr_err("VIF%u.%u: failed to add key: cipher=%x idx=%u pw=%u\n", |
||||
vif->mac->macid, vif->vifid, params->cipher, key_index, |
||||
pairwise); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_del_key(struct wiphy *wiphy, struct net_device *dev, |
||||
u8 key_index, bool pairwise, const u8 *mac_addr) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
ret = qtnf_cmd_send_del_key(vif, key_index, pairwise, mac_addr); |
||||
if (ret) |
||||
pr_err("VIF%u.%u: failed to delete key: idx=%u pw=%u\n", |
||||
vif->mac->macid, vif->vifid, key_index, pairwise); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_set_default_key(struct wiphy *wiphy, struct net_device *dev, |
||||
u8 key_index, bool unicast, bool multicast) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
ret = qtnf_cmd_send_set_default_key(vif, key_index, unicast, multicast); |
||||
if (ret) |
||||
pr_err("VIF%u.%u: failed to set dflt key: idx=%u uc=%u mc=%u\n", |
||||
vif->mac->macid, vif->vifid, key_index, unicast, |
||||
multicast); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int |
||||
qtnf_set_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, |
||||
u8 key_index) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
ret = qtnf_cmd_send_set_default_mgmt_key(vif, key_index); |
||||
if (ret) |
||||
pr_err("VIF%u.%u: failed to set default MGMT key: idx=%u\n", |
||||
vif->mac->macid, vif->vifid, key_index); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int |
||||
qtnf_change_station(struct wiphy *wiphy, struct net_device *dev, |
||||
const u8 *mac, struct station_parameters *params) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
ret = qtnf_cmd_send_change_sta(vif, mac, params); |
||||
if (ret) |
||||
pr_err("VIF%u.%u: failed to change STA %pM\n", |
||||
vif->mac->macid, vif->vifid, mac); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int |
||||
qtnf_del_station(struct wiphy *wiphy, struct net_device *dev, |
||||
struct station_del_parameters *params) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
int ret; |
||||
|
||||
if (params->mac && |
||||
(vif->wdev.iftype == NL80211_IFTYPE_AP) && |
||||
!is_broadcast_ether_addr(params->mac) && |
||||
!qtnf_sta_list_lookup(&vif->sta_list, params->mac)) |
||||
return 0; |
||||
|
||||
qtnf_scan_done(vif->mac, true); |
||||
|
||||
ret = qtnf_cmd_send_del_sta(vif, params); |
||||
if (ret) |
||||
pr_err("VIF%u.%u: failed to delete STA %pM\n", |
||||
vif->mac->macid, vif->vifid, params->mac); |
||||
return ret; |
||||
} |
||||
|
||||
static int |
||||
qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) |
||||
{ |
||||
struct qtnf_wmac *mac = wiphy_priv(wiphy); |
||||
int ret; |
||||
|
||||
mac->scan_req = request; |
||||
|
||||
ret = qtnf_cmd_send_scan(mac); |
||||
if (ret) |
||||
pr_err("MAC%u: failed to start scan\n", mac->macid); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int |
||||
qtnf_connect(struct wiphy *wiphy, struct net_device *dev, |
||||
struct cfg80211_connect_params *sme) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(dev); |
||||
struct qtnf_bss_config *bss_cfg; |
||||
int ret; |
||||
|
||||
if (vif->wdev.iftype != NL80211_IFTYPE_STATION) |
||||
return -EOPNOTSUPP; |
||||
|
||||
if (vif->sta_state != QTNF_STA_DISCONNECTED) |
||||
return -EBUSY; |
||||
|
||||
bss_cfg = &vif->bss_cfg; |
||||
memset(bss_cfg, 0, sizeof(*bss_cfg)); |
||||
|
||||
bss_cfg->ssid_len = sme->ssid_len; |
||||
memcpy(&bss_cfg->ssid, sme->ssid, bss_cfg->ssid_len); |
||||
bss_cfg->chandef.chan = sme->channel; |
||||
bss_cfg->auth_type = sme->auth_type; |
||||
bss_cfg->privacy = sme->privacy; |
||||
bss_cfg->mfp = sme->mfp; |
||||
|
||||
if ((sme->bg_scan_period > 0) && |
||||
(sme->bg_scan_period <= QTNF_MAX_BG_SCAN_PERIOD)) |
||||
bss_cfg->bg_scan_period = sme->bg_scan_period; |
||||
else if (sme->bg_scan_period == -1) |
||||
bss_cfg->bg_scan_period = QTNF_DEFAULT_BG_SCAN_PERIOD; |
||||
else |
||||
bss_cfg->bg_scan_period = 0; /* disabled */ |
||||
|
||||
bss_cfg->connect_flags = 0; |
||||
|
||||
if (sme->flags & ASSOC_REQ_DISABLE_HT) |
||||
bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; |
||||
if (sme->flags & ASSOC_REQ_DISABLE_VHT) |
||||
bss_cfg->connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; |
||||
if (sme->flags & ASSOC_REQ_USE_RRM) |
||||
bss_cfg->connect_flags |= QLINK_STA_CONNECT_USE_RRM; |
||||
|
||||
memcpy(&bss_cfg->crypto, &sme->crypto, sizeof(bss_cfg->crypto)); |
||||
if (sme->bssid) |
||||
ether_addr_copy(bss_cfg->bssid, sme->bssid); |
||||
else |
||||
eth_zero_addr(bss_cfg->bssid); |
||||
|
||||
ret = qtnf_cmd_send_connect(vif, sme); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to connect\n", vif->mac->macid, |
||||
vif->vifid); |
||||
return ret; |
||||
} |
||||
|
||||
vif->sta_state = QTNF_STA_CONNECTING; |
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev, |
||||
u16 reason_code) |
||||
{ |
||||
struct qtnf_wmac *mac = wiphy_priv(wiphy); |
||||
struct qtnf_vif *vif; |
||||
int ret; |
||||
|
||||
vif = qtnf_mac_get_base_vif(mac); |
||||
if (!vif) { |
||||
pr_err("MAC%u: primary VIF is not configured\n", mac->macid); |
||||
return -EFAULT; |
||||
} |
||||
|
||||
if (vif->wdev.iftype != NL80211_IFTYPE_STATION) |
||||
return -EOPNOTSUPP; |
||||
|
||||
if (vif->sta_state == QTNF_STA_DISCONNECTED) |
||||
return 0; |
||||
|
||||
ret = qtnf_cmd_send_disconnect(vif, reason_code); |
||||
if (ret) { |
||||
pr_err("VIF%u.%u: failed to disconnect\n", mac->macid, |
||||
vif->vifid); |
||||
return ret; |
||||
} |
||||
|
||||
vif->sta_state = QTNF_STA_DISCONNECTED; |
||||
return 0; |
||||
} |
||||
|
||||
static struct cfg80211_ops qtn_cfg80211_ops = { |
||||
.add_virtual_intf = qtnf_add_virtual_intf, |
||||
.change_virtual_intf = qtnf_change_virtual_intf, |
||||
.del_virtual_intf = qtnf_del_virtual_intf, |
||||
.start_ap = qtnf_start_ap, |
||||
.change_beacon = qtnf_change_beacon, |
||||
.stop_ap = qtnf_stop_ap, |
||||
.set_wiphy_params = qtnf_set_wiphy_params, |
||||
.mgmt_frame_register = qtnf_mgmt_frame_register, |
||||
.mgmt_tx = qtnf_mgmt_tx, |
||||
.change_station = qtnf_change_station, |
||||
.del_station = qtnf_del_station, |
||||
.get_station = qtnf_get_station, |
||||
.dump_station = qtnf_dump_station, |
||||
.add_key = qtnf_add_key, |
||||
.del_key = qtnf_del_key, |
||||
.set_default_key = qtnf_set_default_key, |
||||
.set_default_mgmt_key = qtnf_set_default_mgmt_key, |
||||
.scan = qtnf_scan, |
||||
.connect = qtnf_connect, |
||||
.disconnect = qtnf_disconnect |
||||
}; |
||||
|
||||
static void qtnf_cfg80211_reg_notifier(struct wiphy *wiphy, |
||||
struct regulatory_request *req) |
||||
{ |
||||
struct qtnf_wmac *mac = wiphy_priv(wiphy); |
||||
struct qtnf_bus *bus; |
||||
struct qtnf_vif *vif; |
||||
struct qtnf_wmac *chan_mac; |
||||
int i; |
||||
enum nl80211_band band; |
||||
|
||||
bus = mac->bus; |
||||
|
||||
pr_debug("MAC%u: initiator=%d alpha=%c%c\n", mac->macid, req->initiator, |
||||
req->alpha2[0], req->alpha2[1]); |
||||
|
||||
vif = qtnf_mac_get_base_vif(mac); |
||||
if (!vif) { |
||||
pr_err("MAC%u: primary VIF is not configured\n", mac->macid); |
||||
return; |
||||
} |
||||
|
||||
/* ignore non-ISO3166 country codes */ |
||||
for (i = 0; i < sizeof(req->alpha2); i++) { |
||||
if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { |
||||
pr_err("MAC%u: not an ISO3166 code\n", mac->macid); |
||||
return; |
||||
} |
||||
} |
||||
if (!strncasecmp(req->alpha2, bus->hw_info.alpha2_code, |
||||
sizeof(req->alpha2))) { |
||||
pr_warn("MAC%u: unchanged country code\n", mac->macid); |
||||
return; |
||||
} |
||||
|
||||
if (qtnf_cmd_send_regulatory_config(mac, req->alpha2)) { |
||||
pr_err("MAC%u: failed to configure regulatory\n", mac->macid); |
||||
return; |
||||
} |
||||
|
||||
for (i = 0; i < bus->hw_info.num_mac; i++) { |
||||
chan_mac = bus->mac[i]; |
||||
|
||||
if (!chan_mac) |
||||
continue; |
||||
|
||||
if (!(bus->hw_info.mac_bitmap & BIT(i))) |
||||
continue; |
||||
|
||||
for (band = 0; band < NUM_NL80211_BANDS; ++band) { |
||||
if (!wiphy->bands[band]) |
||||
continue; |
||||
|
||||
if (qtnf_cmd_get_mac_chan_info(chan_mac, |
||||
wiphy->bands[band])) { |
||||
pr_err("MAC%u: can't get channel info\n", |
||||
chan_mac->macid); |
||||
qtnf_core_detach(bus); |
||||
|
||||
return; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void qtnf_band_setup_htvht_caps(struct qtnf_mac_info *macinfo, |
||||
struct ieee80211_supported_band *band) |
||||
{ |
||||
struct ieee80211_sta_ht_cap *ht_cap; |
||||
struct ieee80211_sta_vht_cap *vht_cap; |
||||
|
||||
ht_cap = &band->ht_cap; |
||||
ht_cap->ht_supported = true; |
||||
memcpy(&ht_cap->cap, &macinfo->ht_cap.cap_info, |
||||
sizeof(u16)); |
||||
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; |
||||
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; |
||||
memcpy(&ht_cap->mcs, &macinfo->ht_cap.mcs, |
||||
sizeof(ht_cap->mcs)); |
||||
|
||||
if (macinfo->phymode_cap & QLINK_PHYMODE_AC) { |
||||
vht_cap = &band->vht_cap; |
||||
vht_cap->vht_supported = true; |
||||
memcpy(&vht_cap->cap, |
||||
&macinfo->vht_cap.vht_cap_info, sizeof(u32)); |
||||
/* Update MCS support for VHT */ |
||||
memcpy(&vht_cap->vht_mcs, |
||||
&macinfo->vht_cap.supp_mcs, |
||||
sizeof(struct ieee80211_vht_mcs_info)); |
||||
} |
||||
} |
||||
|
||||
struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus) |
||||
{ |
||||
struct wiphy *wiphy; |
||||
|
||||
wiphy = wiphy_new(&qtn_cfg80211_ops, sizeof(struct qtnf_wmac)); |
||||
if (!wiphy) |
||||
return NULL; |
||||
|
||||
set_wiphy_dev(wiphy, bus->dev); |
||||
|
||||
return wiphy; |
||||
} |
||||
|
||||
static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, |
||||
struct ieee80211_iface_combination *if_comb, |
||||
const struct qtnf_mac_info *mac_info) |
||||
{ |
||||
size_t max_interfaces = 0; |
||||
u16 interface_modes = 0; |
||||
size_t i; |
||||
|
||||
if (unlikely(!mac_info->limits || !mac_info->n_limits)) |
||||
return -ENOENT; |
||||
|
||||
if_comb->limits = mac_info->limits; |
||||
if_comb->n_limits = mac_info->n_limits; |
||||
|
||||
for (i = 0; i < mac_info->n_limits; i++) { |
||||
max_interfaces += mac_info->limits[i].max; |
||||
interface_modes |= mac_info->limits[i].types; |
||||
} |
||||
|
||||
if_comb->num_different_channels = 1; |
||||
if_comb->beacon_int_infra_match = true; |
||||
if_comb->max_interfaces = max_interfaces; |
||||
if_comb->radar_detect_widths = mac_info->radar_detect_widths; |
||||
wiphy->interface_modes = interface_modes; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac) |
||||
{ |
||||
struct wiphy *wiphy = priv_to_wiphy(mac); |
||||
struct ieee80211_iface_combination *iface_comb = NULL; |
||||
int ret; |
||||
|
||||
if (!wiphy) { |
||||
pr_err("invalid wiphy pointer\n"); |
||||
return -EFAULT; |
||||
} |
||||
|
||||
iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL); |
||||
if (!iface_comb) { |
||||
ret = -ENOMEM; |
||||
goto out; |
||||
} |
||||
|
||||
ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo); |
||||
if (ret) |
||||
goto out; |
||||
|
||||
pr_info("MAC%u: phymode=%#x radar=%#x\n", mac->macid, |
||||
mac->macinfo.phymode_cap, mac->macinfo.radar_detect_widths); |
||||
|
||||
wiphy->frag_threshold = mac->macinfo.frag_thr; |
||||
wiphy->rts_threshold = mac->macinfo.rts_thr; |
||||
wiphy->retry_short = mac->macinfo.sretry_limit; |
||||
wiphy->retry_long = mac->macinfo.lretry_limit; |
||||
wiphy->coverage_class = mac->macinfo.coverage_class; |
||||
|
||||
wiphy->max_scan_ssids = QTNF_MAX_SSID_LIST_LENGTH; |
||||
wiphy->max_scan_ie_len = QTNF_MAX_VSIE_LEN; |
||||
wiphy->mgmt_stypes = qtnf_mgmt_stypes; |
||||
wiphy->max_remain_on_channel_duration = 5000; |
||||
|
||||
wiphy->iface_combinations = iface_comb; |
||||
wiphy->n_iface_combinations = 1; |
||||
|
||||
/* Initialize cipher suits */ |
||||
wiphy->cipher_suites = qtnf_cipher_suites; |
||||
wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites); |
||||
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; |
||||
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | |
||||
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | |
||||
WIPHY_FLAG_AP_UAPSD; |
||||
|
||||
wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | |
||||
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2; |
||||
|
||||
wiphy->available_antennas_tx = mac->macinfo.num_tx_chain; |
||||
wiphy->available_antennas_rx = mac->macinfo.num_rx_chain; |
||||
|
||||
wiphy->max_ap_assoc_sta = mac->macinfo.max_ap_assoc_sta; |
||||
|
||||
ether_addr_copy(wiphy->perm_addr, mac->macaddr); |
||||
|
||||
if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) { |
||||
pr_debug("device supports REG_UPDATE\n"); |
||||
wiphy->reg_notifier = qtnf_cfg80211_reg_notifier; |
||||
pr_debug("hint regulatory about EP region: %c%c\n", |
||||
hw_info->alpha2_code[0], |
||||
hw_info->alpha2_code[1]); |
||||
regulatory_hint(wiphy, hw_info->alpha2_code); |
||||
} else { |
||||
pr_debug("device doesn't support REG_UPDATE\n"); |
||||
wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; |
||||
} |
||||
|
||||
ret = wiphy_register(wiphy); |
||||
|
||||
out: |
||||
if (ret < 0) { |
||||
kfree(iface_comb); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void qtnf_netdev_updown(struct net_device *ndev, bool up) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); |
||||
|
||||
if (qtnf_cmd_send_updown_intf(vif, up)) |
||||
pr_err("failed to send up/down command to FW\n"); |
||||
} |
||||
|
||||
void qtnf_virtual_intf_cleanup(struct net_device *ndev) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); |
||||
struct qtnf_wmac *mac = wiphy_priv(vif->wdev.wiphy); |
||||
|
||||
if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { |
||||
switch (vif->sta_state) { |
||||
case QTNF_STA_DISCONNECTED: |
||||
break; |
||||
case QTNF_STA_CONNECTING: |
||||
cfg80211_connect_result(vif->netdev, |
||||
vif->bss_cfg.bssid, NULL, 0, |
||||
NULL, 0, |
||||
WLAN_STATUS_UNSPECIFIED_FAILURE, |
||||
GFP_KERNEL); |
||||
qtnf_disconnect(vif->wdev.wiphy, ndev, |
||||
WLAN_REASON_DEAUTH_LEAVING); |
||||
break; |
||||
case QTNF_STA_CONNECTED: |
||||
cfg80211_disconnected(vif->netdev, |
||||
WLAN_REASON_DEAUTH_LEAVING, |
||||
NULL, 0, 1, GFP_KERNEL); |
||||
qtnf_disconnect(vif->wdev.wiphy, ndev, |
||||
WLAN_REASON_DEAUTH_LEAVING); |
||||
break; |
||||
} |
||||
|
||||
vif->sta_state = QTNF_STA_DISCONNECTED; |
||||
qtnf_scan_done(mac, true); |
||||
} |
||||
} |
||||
|
||||
void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif) |
||||
{ |
||||
if (vif->wdev.iftype == NL80211_IFTYPE_STATION) { |
||||
switch (vif->sta_state) { |
||||
case QTNF_STA_CONNECTING: |
||||
cfg80211_connect_result(vif->netdev, |
||||
vif->bss_cfg.bssid, NULL, 0, |
||||
NULL, 0, |
||||
WLAN_STATUS_UNSPECIFIED_FAILURE, |
||||
GFP_KERNEL); |
||||
break; |
||||
case QTNF_STA_CONNECTED: |
||||
cfg80211_disconnected(vif->netdev, |
||||
WLAN_REASON_DEAUTH_LEAVING, |
||||
NULL, 0, 1, GFP_KERNEL); |
||||
break; |
||||
case QTNF_STA_DISCONNECTED: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
cfg80211_shutdown_all_interfaces(vif->wdev.wiphy); |
||||
vif->sta_state = QTNF_STA_DISCONNECTED; |
||||
} |
||||
|
||||
void qtnf_band_init_rates(struct ieee80211_supported_band *band) |
||||
{ |
||||
switch (band->band) { |
||||
case NL80211_BAND_2GHZ: |
||||
band->bitrates = qtnf_rates_2g; |
||||
band->n_bitrates = ARRAY_SIZE(qtnf_rates_2g); |
||||
break; |
||||
case NL80211_BAND_5GHZ: |
||||
band->bitrates = qtnf_rates_5g; |
||||
band->n_bitrates = ARRAY_SIZE(qtnf_rates_5g); |
||||
break; |
||||
default: |
||||
band->bitrates = NULL; |
||||
band->n_bitrates = 0; |
||||
break; |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_CFG80211_H_ |
||||
#define _QTN_FMAC_CFG80211_H_ |
||||
|
||||
#include <net/cfg80211.h> |
||||
|
||||
#include "core.h" |
||||
|
||||
int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac); |
||||
int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); |
||||
void qtnf_cfg80211_vif_reset(struct qtnf_vif *vif); |
||||
void qtnf_band_init_rates(struct ieee80211_supported_band *band); |
||||
void qtnf_band_setup_htvht_caps(struct qtnf_mac_info *macinfo, |
||||
struct ieee80211_supported_band *band); |
||||
|
||||
static inline void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted) |
||||
{ |
||||
struct cfg80211_scan_info info = { |
||||
.aborted = aborted, |
||||
}; |
||||
|
||||
if (mac->scan_req) { |
||||
cfg80211_scan_done(mac->scan_req, &info); |
||||
mac->scan_req = NULL; |
||||
} |
||||
} |
||||
|
||||
#endif /* _QTN_FMAC_CFG80211_H_ */ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,74 @@ |
||||
/*
|
||||
* Copyright (c) 2016 Quantenna Communications, Inc. |
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef QLINK_COMMANDS_H_ |
||||
#define QLINK_COMMANDS_H_ |
||||
|
||||
#include <linux/nl80211.h> |
||||
|
||||
#include "core.h" |
||||
#include "bus.h" |
||||
|
||||
int qtnf_cmd_send_init_fw(struct qtnf_bus *bus); |
||||
void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus); |
||||
int qtnf_cmd_get_hw_info(struct qtnf_bus *bus); |
||||
int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac); |
||||
int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype, |
||||
u8 *mac_addr); |
||||
int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif, |
||||
enum nl80211_iftype iftype, u8 *mac_addr); |
||||
int qtnf_cmd_send_del_intf(struct qtnf_vif *vif); |
||||
int qtnf_cmd_get_mac_chan_info(struct qtnf_wmac *mac, |
||||
struct ieee80211_supported_band *band); |
||||
int qtnf_cmd_send_regulatory_config(struct qtnf_wmac *mac, const char *alpha2); |
||||
int qtnf_cmd_send_config_ap(struct qtnf_vif *vif); |
||||
int qtnf_cmd_send_start_ap(struct qtnf_vif *vif); |
||||
int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif); |
||||
int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg); |
||||
int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags, |
||||
u16 freq, const u8 *buf, size_t len); |
||||
int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type, |
||||
const u8 *buf, size_t len); |
||||
int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac, |
||||
struct station_info *sinfo); |
||||
int qtnf_cmd_send_phy_params(struct qtnf_wmac *mac, u16 cmd_action, |
||||
void *data_buf); |
||||
int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, |
||||
const u8 *mac_addr, struct key_params *params); |
||||
int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, |
||||
const u8 *mac_addr); |
||||
int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index, |
||||
bool unicast, bool multicast); |
||||
int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index); |
||||
int qtnf_cmd_send_add_sta(struct qtnf_vif *vif, const u8 *mac, |
||||
struct station_parameters *params); |
||||
int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, |
||||
struct station_parameters *params); |
||||
int qtnf_cmd_send_del_sta(struct qtnf_vif *vif, |
||||
struct station_del_parameters *params); |
||||
|
||||
int qtnf_cmd_resp_parse(struct qtnf_bus *bus, struct sk_buff *resp_skb); |
||||
int qtnf_cmd_resp_check(const struct qtnf_vif *vif, |
||||
const struct sk_buff *resp_skb, u16 cmd_id, |
||||
u16 *result, const u8 **payload, size_t *payload_size); |
||||
int qtnf_cmd_send_scan(struct qtnf_wmac *mac); |
||||
int qtnf_cmd_send_connect(struct qtnf_vif *vif, |
||||
struct cfg80211_connect_params *sme); |
||||
int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, |
||||
u16 reason_code); |
||||
int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, |
||||
bool up); |
||||
|
||||
#endif /* QLINK_COMMANDS_H_ */ |
@ -0,0 +1,618 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/module.h> |
||||
#include <linux/if_ether.h> |
||||
|
||||
#include "core.h" |
||||
#include "bus.h" |
||||
#include "trans.h" |
||||
#include "commands.h" |
||||
#include "cfg80211.h" |
||||
#include "event.h" |
||||
#include "util.h" |
||||
|
||||
#define QTNF_DMP_MAX_LEN 48 |
||||
#define QTNF_PRIMARY_VIF_IDX 0 |
||||
|
||||
struct qtnf_frame_meta_info { |
||||
u8 magic_s; |
||||
u8 ifidx; |
||||
u8 macid; |
||||
u8 magic_e; |
||||
} __packed; |
||||
|
||||
struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid) |
||||
{ |
||||
struct qtnf_wmac *mac = NULL; |
||||
|
||||
if (unlikely(macid >= QTNF_MAX_MAC)) { |
||||
pr_err("invalid MAC index %u\n", macid); |
||||
return NULL; |
||||
} |
||||
|
||||
mac = bus->mac[macid]; |
||||
|
||||
if (unlikely(!mac)) { |
||||
pr_err("MAC%u: not initialized\n", macid); |
||||
return NULL; |
||||
} |
||||
|
||||
return mac; |
||||
} |
||||
|
||||
/* Netdev handler for open.
|
||||
*/ |
||||
static int qtnf_netdev_open(struct net_device *ndev) |
||||
{ |
||||
netif_carrier_off(ndev); |
||||
qtnf_netdev_updown(ndev, 1); |
||||
return 0; |
||||
} |
||||
|
||||
/* Netdev handler for close.
|
||||
*/ |
||||
static int qtnf_netdev_close(struct net_device *ndev) |
||||
{ |
||||
netif_carrier_off(ndev); |
||||
qtnf_virtual_intf_cleanup(ndev); |
||||
qtnf_netdev_updown(ndev, 0); |
||||
return 0; |
||||
} |
||||
|
||||
/* Netdev handler for data transmission.
|
||||
*/ |
||||
static int |
||||
qtnf_netdev_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) |
||||
{ |
||||
struct qtnf_vif *vif; |
||||
struct qtnf_wmac *mac; |
||||
|
||||
vif = qtnf_netdev_get_priv(ndev); |
||||
|
||||
if (unlikely(skb->dev != ndev)) { |
||||
pr_err_ratelimited("invalid skb->dev"); |
||||
dev_kfree_skb_any(skb); |
||||
return 0; |
||||
} |
||||
|
||||
if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)) { |
||||
pr_err_ratelimited("%s: VIF not initialized\n", ndev->name); |
||||
dev_kfree_skb_any(skb); |
||||
return 0; |
||||
} |
||||
|
||||
mac = vif->mac; |
||||
if (unlikely(!mac)) { |
||||
pr_err_ratelimited("%s: NULL mac pointer", ndev->name); |
||||
dev_kfree_skb_any(skb); |
||||
return 0; |
||||
} |
||||
|
||||
if (!skb->len || (skb->len > ETH_FRAME_LEN)) { |
||||
pr_err_ratelimited("%s: invalid skb len %d\n", ndev->name, |
||||
skb->len); |
||||
dev_kfree_skb_any(skb); |
||||
ndev->stats.tx_dropped++; |
||||
return 0; |
||||
} |
||||
|
||||
/* tx path is enabled: reset vif timeout */ |
||||
vif->cons_tx_timeout_cnt = 0; |
||||
|
||||
return qtnf_bus_data_tx(mac->bus, skb); |
||||
} |
||||
|
||||
/* Netdev handler for getting stats.
|
||||
*/ |
||||
static struct net_device_stats *qtnf_netdev_get_stats(struct net_device *dev) |
||||
{ |
||||
return &dev->stats; |
||||
} |
||||
|
||||
/* Netdev handler for transmission timeout.
|
||||
*/ |
||||
static void qtnf_netdev_tx_timeout(struct net_device *ndev) |
||||
{ |
||||
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev); |
||||
struct qtnf_wmac *mac; |
||||
struct qtnf_bus *bus; |
||||
|
||||
if (unlikely(!vif || !vif->mac || !vif->mac->bus)) |
||||
return; |
||||
|
||||
mac = vif->mac; |
||||
bus = mac->bus; |
||||
|
||||
pr_warn("VIF%u.%u: Tx timeout- %lu\n", mac->macid, vif->vifid, jiffies); |
||||
|
||||
qtnf_bus_data_tx_timeout(bus, ndev); |
||||
ndev->stats.tx_errors++; |
||||
|
||||
if (++vif->cons_tx_timeout_cnt > QTNF_TX_TIMEOUT_TRSHLD) { |
||||
pr_err("Tx timeout threshold exceeded !\n"); |
||||
pr_err("schedule interface %s reset !\n", netdev_name(ndev)); |
||||
queue_work(bus->workqueue, &vif->reset_work); |
||||
} |
||||
} |
||||
|
||||
/* Network device ops handlers */ |
||||
const struct net_device_ops qtnf_netdev_ops = { |
||||
.ndo_open = qtnf_netdev_open, |
||||
.ndo_stop = qtnf_netdev_close, |
||||
.ndo_start_xmit = qtnf_netdev_hard_start_xmit, |
||||
.ndo_tx_timeout = qtnf_netdev_tx_timeout, |
||||
.ndo_get_stats = qtnf_netdev_get_stats, |
||||
}; |
||||
|
||||
static int qtnf_mac_init_single_band(struct wiphy *wiphy, |
||||
struct qtnf_wmac *mac, |
||||
enum nl80211_band band) |
||||
{ |
||||
int ret; |
||||
|
||||
wiphy->bands[band] = kzalloc(sizeof(*wiphy->bands[band]), GFP_KERNEL); |
||||
if (!wiphy->bands[band]) |
||||
return -ENOMEM; |
||||
|
||||
wiphy->bands[band]->band = band; |
||||
|
||||
ret = qtnf_cmd_get_mac_chan_info(mac, wiphy->bands[band]); |
||||
if (ret) { |
||||
pr_err("MAC%u: band %u: failed to get chans info: %d\n", |
||||
mac->macid, band, ret); |
||||
return ret; |
||||
} |
||||
|
||||
qtnf_band_init_rates(wiphy->bands[band]); |
||||
qtnf_band_setup_htvht_caps(&mac->macinfo, wiphy->bands[band]); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int qtnf_mac_init_bands(struct qtnf_wmac *mac) |
||||
{ |
||||
struct wiphy *wiphy = priv_to_wiphy(mac); |
||||
int ret = 0; |
||||
|
||||
if (mac->macinfo.bands_cap & QLINK_BAND_2GHZ) { |
||||
ret = qtnf_mac_init_single_band(wiphy, mac, NL80211_BAND_2GHZ); |
||||
if (ret) |
||||
goto out; |
||||
} |
||||
|
||||
if (mac->macinfo.bands_cap & QLINK_BAND_5GHZ) { |
||||
ret = qtnf_mac_init_single_band(wiphy, mac, NL80211_BAND_5GHZ); |
||||
if (ret) |
||||
goto out; |
||||
} |
||||
|
||||
if (mac->macinfo.bands_cap & QLINK_BAND_60GHZ) |
||||
ret = qtnf_mac_init_single_band(wiphy, mac, NL80211_BAND_60GHZ); |
||||
|
||||
out: |
||||
return ret; |
||||
} |
||||
|
||||
struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac) |
||||
{ |
||||
struct qtnf_vif *vif; |
||||
int i; |
||||
|
||||
for (i = 0; i < QTNF_MAX_INTF; i++) { |
||||
vif = &mac->iflist[i]; |
||||
if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) |
||||
return vif; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac) |
||||
{ |
||||
struct qtnf_vif *vif; |
||||
|
||||
vif = &mac->iflist[QTNF_PRIMARY_VIF_IDX]; |
||||
|
||||
if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) |
||||
return NULL; |
||||
|
||||
return vif; |
||||
} |
||||
|
||||
static void qtnf_vif_reset_handler(struct work_struct *work) |
||||
{ |
||||
struct qtnf_vif *vif = container_of(work, struct qtnf_vif, reset_work); |
||||
|
||||
rtnl_lock(); |
||||
|
||||
if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) { |
||||
rtnl_unlock(); |
||||
return; |
||||
} |
||||
|
||||
/* stop tx completely */ |
||||
netif_tx_stop_all_queues(vif->netdev); |
||||
if (netif_carrier_ok(vif->netdev)) |
||||
netif_carrier_off(vif->netdev); |
||||
|
||||
qtnf_cfg80211_vif_reset(vif); |
||||
|
||||
rtnl_unlock(); |
||||
} |
||||
|
||||
static void qtnf_mac_init_primary_intf(struct qtnf_wmac *mac) |
||||
{ |
||||
struct qtnf_vif *vif = &mac->iflist[QTNF_PRIMARY_VIF_IDX]; |
||||
|
||||
vif->wdev.iftype = NL80211_IFTYPE_AP; |
||||
vif->bss_priority = QTNF_DEF_BSS_PRIORITY; |
||||
vif->wdev.wiphy = priv_to_wiphy(mac); |
||||
INIT_WORK(&vif->reset_work, qtnf_vif_reset_handler); |
||||
vif->cons_tx_timeout_cnt = 0; |
||||
} |
||||
|
||||
static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus, |
||||
unsigned int macid) |
||||
{ |
||||
struct wiphy *wiphy; |
||||
struct qtnf_wmac *mac; |
||||
unsigned int i; |
||||
|
||||
wiphy = qtnf_wiphy_allocate(bus); |
||||
if (!wiphy) |
||||
return ERR_PTR(-ENOMEM); |
||||
|
||||
mac = wiphy_priv(wiphy); |
||||
|
||||
mac->macid = macid; |
||||
mac->bus = bus; |
||||
|
||||
for (i = 0; i < QTNF_MAX_INTF; i++) { |
||||
memset(&mac->iflist[i], 0, sizeof(struct qtnf_vif)); |
||||
mac->iflist[i].wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; |
||||
mac->iflist[i].mac = mac; |
||||
mac->iflist[i].vifid = i; |
||||
qtnf_sta_list_init(&mac->iflist[i].sta_list); |
||||
} |
||||
|
||||
qtnf_mac_init_primary_intf(mac); |
||||
bus->mac[macid] = mac; |
||||
|
||||
return mac; |
||||
} |
||||
|
||||
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif, |
||||
const char *name, unsigned char name_assign_type, |
||||
enum nl80211_iftype iftype) |
||||
{ |
||||
struct wiphy *wiphy = priv_to_wiphy(mac); |
||||
struct net_device *dev; |
||||
void *qdev_vif; |
||||
int ret; |
||||
|
||||
dev = alloc_netdev_mqs(sizeof(struct qtnf_vif *), name, |
||||
name_assign_type, ether_setup, 1, 1); |
||||
if (!dev) { |
||||
memset(&vif->wdev, 0, sizeof(vif->wdev)); |
||||
vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
vif->netdev = dev; |
||||
|
||||
dev->netdev_ops = &qtnf_netdev_ops; |
||||
dev->destructor = free_netdev; |
||||
dev_net_set(dev, wiphy_net(wiphy)); |
||||
dev->ieee80211_ptr = &vif->wdev; |
||||
dev->ieee80211_ptr->iftype = iftype; |
||||
ether_addr_copy(dev->dev_addr, vif->mac_addr); |
||||
SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); |
||||
dev->flags |= IFF_BROADCAST | IFF_MULTICAST; |
||||
dev->watchdog_timeo = QTNF_DEF_WDOG_TIMEOUT; |
||||
dev->tx_queue_len = 100; |
||||
|
||||
qdev_vif = netdev_priv(dev); |
||||
*((void **)qdev_vif) = vif; |
||||
|
||||
SET_NETDEV_DEV(dev, mac->bus->dev); |
||||
|
||||
ret = register_netdevice(dev); |
||||
if (ret) { |
||||
free_netdev(dev); |
||||
vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid) |
||||
{ |
||||
struct qtnf_wmac *mac; |
||||
struct wiphy *wiphy; |
||||
struct qtnf_vif *vif; |
||||
unsigned int i; |
||||
enum nl80211_band band; |
||||
|
||||
mac = bus->mac[macid]; |
||||
|
||||
if (!mac) |
||||
return; |
||||
|
||||
wiphy = priv_to_wiphy(mac); |
||||
|
||||
for (i = 0; i < QTNF_MAX_INTF; i++) { |
||||
vif = &mac->iflist[i]; |
||||
rtnl_lock(); |
||||
if (vif->netdev && |
||||
vif->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) { |
||||
qtnf_virtual_intf_cleanup(vif->netdev); |
||||
qtnf_del_virtual_intf(wiphy, &vif->wdev); |
||||
} |
||||
rtnl_unlock(); |
||||
qtnf_sta_list_free(&vif->sta_list); |
||||
} |
||||
|
||||
if (mac->wiphy_registered) |
||||
wiphy_unregister(wiphy); |
||||
|
||||
for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; ++band) { |
||||
if (!wiphy->bands[band]) |
||||
continue; |
||||
|
||||
kfree(wiphy->bands[band]->channels); |
||||
wiphy->bands[band]->n_channels = 0; |
||||
|
||||
kfree(wiphy->bands[band]); |
||||
wiphy->bands[band] = NULL; |
||||
} |
||||
|
||||
kfree(mac->macinfo.limits); |
||||
kfree(wiphy->iface_combinations); |
||||
wiphy_free(wiphy); |
||||
bus->mac[macid] = NULL; |
||||
} |
||||
|
||||
static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid) |
||||
{ |
||||
struct qtnf_wmac *mac; |
||||
struct qtnf_vif *vif; |
||||
int ret; |
||||
|
||||
if (!(bus->hw_info.mac_bitmap & BIT(macid))) { |
||||
pr_info("MAC%u is not active in FW\n", macid); |
||||
return 0; |
||||
} |
||||
|
||||
mac = qtnf_core_mac_alloc(bus, macid); |
||||
if (IS_ERR(mac)) { |
||||
pr_err("MAC%u allocation failed\n", macid); |
||||
return PTR_ERR(mac); |
||||
} |
||||
|
||||
ret = qtnf_cmd_get_mac_info(mac); |
||||
if (ret) { |
||||
pr_err("MAC%u: failed to get info\n", macid); |
||||
goto error; |
||||
} |
||||
|
||||
vif = qtnf_mac_get_base_vif(mac); |
||||
if (!vif) { |
||||
pr_err("MAC%u: primary VIF is not ready\n", macid); |
||||
ret = -EFAULT; |
||||
goto error; |
||||
} |
||||
|
||||
ret = qtnf_cmd_send_add_intf(vif, NL80211_IFTYPE_AP, vif->mac_addr); |
||||
if (ret) { |
||||
pr_err("MAC%u: failed to add VIF\n", macid); |
||||
goto error; |
||||
} |
||||
|
||||
ret = qtnf_cmd_send_get_phy_params(mac); |
||||
if (ret) { |
||||
pr_err("MAC%u: failed to get PHY settings\n", macid); |
||||
goto error; |
||||
} |
||||
|
||||
ret = qtnf_mac_init_bands(mac); |
||||
if (ret) { |
||||
pr_err("MAC%u: failed to init bands\n", macid); |
||||
goto error; |
||||
} |
||||
|
||||
ret = qtnf_wiphy_register(&bus->hw_info, mac); |
||||
if (ret) { |
||||
pr_err("MAC%u: wiphy registration failed\n", macid); |
||||
goto error; |
||||
} |
||||
|
||||
mac->wiphy_registered = 1; |
||||
|
||||
rtnl_lock(); |
||||
|
||||
ret = qtnf_core_net_attach(mac, vif, "wlan%d", NET_NAME_ENUM, |
||||
NL80211_IFTYPE_AP); |
||||
rtnl_unlock(); |
||||
|
||||
if (ret) { |
||||
pr_err("MAC%u: failed to attach netdev\n", macid); |
||||
vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; |
||||
vif->netdev = NULL; |
||||
goto error; |
||||
} |
||||
|
||||
pr_debug("MAC%u initialized\n", macid); |
||||
|
||||
return 0; |
||||
|
||||
error: |
||||
qtnf_core_mac_detach(bus, macid); |
||||
return ret; |
||||
} |
||||
|
||||
int qtnf_core_attach(struct qtnf_bus *bus) |
||||
{ |
||||
unsigned int i; |
||||
int ret; |
||||
|
||||
qtnf_trans_init(bus); |
||||
|
||||
bus->fw_state = QTNF_FW_STATE_BOOT_DONE; |
||||
qtnf_bus_data_rx_start(bus); |
||||
|
||||
bus->workqueue = alloc_ordered_workqueue("QTNF_BUS", 0); |
||||
if (!bus->workqueue) { |
||||
pr_err("failed to alloc main workqueue\n"); |
||||
ret = -ENOMEM; |
||||
goto error; |
||||
} |
||||
|
||||
INIT_WORK(&bus->event_work, qtnf_event_work_handler); |
||||
|
||||
ret = qtnf_cmd_send_init_fw(bus); |
||||
if (ret) { |
||||
pr_err("failed to init FW: %d\n", ret); |
||||
goto error; |
||||
} |
||||
|
||||
bus->fw_state = QTNF_FW_STATE_ACTIVE; |
||||
|
||||
ret = qtnf_cmd_get_hw_info(bus); |
||||
if (ret) { |
||||
pr_err("failed to get HW info: %d\n", ret); |
||||
goto error; |
||||
} |
||||
|
||||
if (bus->hw_info.ql_proto_ver != QLINK_PROTO_VER) { |
||||
pr_err("qlink version mismatch %u != %u\n", |
||||
QLINK_PROTO_VER, bus->hw_info.ql_proto_ver); |
||||
ret = -EPROTONOSUPPORT; |
||||
goto error; |
||||
} |
||||
|
||||
if (bus->hw_info.num_mac > QTNF_MAX_MAC) { |
||||
pr_err("no support for number of MACs=%u\n", |
||||
bus->hw_info.num_mac); |
||||
ret = -ERANGE; |
||||
goto error; |
||||
} |
||||
|
||||
for (i = 0; i < bus->hw_info.num_mac; i++) { |
||||
ret = qtnf_core_mac_attach(bus, i); |
||||
|
||||
if (ret) { |
||||
pr_err("MAC%u: attach failed: %d\n", i, ret); |
||||
goto error; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
error: |
||||
qtnf_core_detach(bus); |
||||
|
||||
return ret; |
||||
} |
||||
EXPORT_SYMBOL_GPL(qtnf_core_attach); |
||||
|
||||
void qtnf_core_detach(struct qtnf_bus *bus) |
||||
{ |
||||
unsigned int macid; |
||||
|
||||
qtnf_bus_data_rx_stop(bus); |
||||
|
||||
for (macid = 0; macid < QTNF_MAX_MAC; macid++) |
||||
qtnf_core_mac_detach(bus, macid); |
||||
|
||||
if (bus->fw_state == QTNF_FW_STATE_ACTIVE) |
||||
qtnf_cmd_send_deinit_fw(bus); |
||||
|
||||
bus->fw_state = QTNF_FW_STATE_DEAD; |
||||
|
||||
if (bus->workqueue) { |
||||
flush_workqueue(bus->workqueue); |
||||
destroy_workqueue(bus->workqueue); |
||||
} |
||||
|
||||
qtnf_trans_free(bus); |
||||
} |
||||
EXPORT_SYMBOL_GPL(qtnf_core_detach); |
||||
|
||||
static inline int qtnf_is_frame_meta_magic_valid(struct qtnf_frame_meta_info *m) |
||||
{ |
||||
return m->magic_s == 0xAB && m->magic_e == 0xBA; |
||||
} |
||||
|
||||
struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb) |
||||
{ |
||||
struct qtnf_frame_meta_info *meta; |
||||
struct net_device *ndev = NULL; |
||||
struct qtnf_wmac *mac; |
||||
struct qtnf_vif *vif; |
||||
|
||||
meta = (struct qtnf_frame_meta_info *) |
||||
(skb_tail_pointer(skb) - sizeof(*meta)); |
||||
|
||||
if (unlikely(!qtnf_is_frame_meta_magic_valid(meta))) { |
||||
pr_err_ratelimited("invalid magic 0x%x:0x%x\n", |
||||
meta->magic_s, meta->magic_e); |
||||
goto out; |
||||
} |
||||
|
||||
if (unlikely(meta->macid >= QTNF_MAX_MAC)) { |
||||
pr_err_ratelimited("invalid mac(%u)\n", meta->macid); |
||||
goto out; |
||||
} |
||||
|
||||
if (unlikely(meta->ifidx >= QTNF_MAX_INTF)) { |
||||
pr_err_ratelimited("invalid vif(%u)\n", meta->ifidx); |
||||
goto out; |
||||
} |
||||
|
||||
mac = bus->mac[meta->macid]; |
||||
|
||||
if (unlikely(!mac)) { |
||||
pr_err_ratelimited("mac(%d) does not exist\n", meta->macid); |
||||
goto out; |
||||
} |
||||
|
||||
vif = &mac->iflist[meta->ifidx]; |
||||
|
||||
if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)) { |
||||
pr_err_ratelimited("vif(%u) does not exists\n", meta->ifidx); |
||||
goto out; |
||||
} |
||||
|
||||
ndev = vif->netdev; |
||||
|
||||
if (unlikely(!ndev)) { |
||||
pr_err_ratelimited("netdev for wlan%u.%u does not exists\n", |
||||
meta->macid, meta->ifidx); |
||||
goto out; |
||||
} |
||||
|
||||
__skb_trim(skb, skb->len - sizeof(*meta)); |
||||
|
||||
out: |
||||
return ndev; |
||||
} |
||||
EXPORT_SYMBOL_GPL(qtnf_classify_skb); |
||||
|
||||
MODULE_AUTHOR("Quantenna Communications"); |
||||
MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver."); |
||||
MODULE_LICENSE("GPL"); |
@ -0,0 +1,173 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_CORE_H_ |
||||
#define _QTN_FMAC_CORE_H_ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/module.h> |
||||
#include <linux/sched.h> |
||||
#include <linux/semaphore.h> |
||||
#include <linux/ip.h> |
||||
#include <linux/skbuff.h> |
||||
#include <linux/if_arp.h> |
||||
#include <linux/etherdevice.h> |
||||
#include <net/sock.h> |
||||
#include <net/lib80211.h> |
||||
#include <net/cfg80211.h> |
||||
#include <linux/vmalloc.h> |
||||
#include <linux/firmware.h> |
||||
#include <linux/ctype.h> |
||||
#include <linux/workqueue.h> |
||||
#include <linux/slab.h> |
||||
|
||||
#include "qlink.h" |
||||
#include "trans.h" |
||||
|
||||
#undef pr_fmt |
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ |
||||
|
||||
#define QTNF_MAX_SSID_LIST_LENGTH 2 |
||||
#define QTNF_MAX_VSIE_LEN 255 |
||||
#define QTNF_MAX_ALPHA_LEN 2 |
||||
#define QTNF_MAX_INTF 8 |
||||
#define QTNF_MAX_EVENT_QUEUE_LEN 255 |
||||
#define QTNF_DEFAULT_BG_SCAN_PERIOD 300 |
||||
#define QTNF_MAX_BG_SCAN_PERIOD 0xffff |
||||
|
||||
#define QTNF_DEF_BSS_PRIORITY 0 |
||||
#define QTNF_DEF_WDOG_TIMEOUT 5 |
||||
#define QTNF_TX_TIMEOUT_TRSHLD 100 |
||||
|
||||
#define QTNF_STATE_AP_CONFIG BIT(2) |
||||
#define QTNF_STATE_AP_START BIT(1) |
||||
|
||||
extern const struct net_device_ops qtnf_netdev_ops; |
||||
struct qtnf_bus; |
||||
struct qtnf_vif; |
||||
|
||||
struct qtnf_bss_config { |
||||
u8 ssid[IEEE80211_MAX_SSID_LEN]; |
||||
u8 bssid[ETH_ALEN]; |
||||
size_t ssid_len; |
||||
u8 dtim; |
||||
u16 bcn_period; |
||||
u16 auth_type; |
||||
bool privacy; |
||||
enum nl80211_mfp mfp; |
||||
struct cfg80211_chan_def chandef; |
||||
struct cfg80211_crypto_settings crypto; |
||||
u16 bg_scan_period; |
||||
u32 connect_flags; |
||||
}; |
||||
|
||||
struct qtnf_sta_node { |
||||
struct list_head list; |
||||
u8 mac_addr[ETH_ALEN]; |
||||
}; |
||||
|
||||
struct qtnf_sta_list { |
||||
struct list_head head; |
||||
atomic_t size; |
||||
}; |
||||
|
||||
enum qtnf_sta_state { |
||||
QTNF_STA_DISCONNECTED, |
||||
QTNF_STA_CONNECTING, |
||||
QTNF_STA_CONNECTED |
||||
}; |
||||
|
||||
struct qtnf_vif { |
||||
struct wireless_dev wdev; |
||||
u8 vifid; |
||||
u8 bss_priority; |
||||
u8 bss_status; |
||||
enum qtnf_sta_state sta_state; |
||||
u16 mgmt_frames_bitmask; |
||||
struct net_device *netdev; |
||||
struct qtnf_wmac *mac; |
||||
u8 mac_addr[ETH_ALEN]; |
||||
struct work_struct reset_work; |
||||
struct qtnf_bss_config bss_cfg; |
||||
struct qtnf_sta_list sta_list; |
||||
unsigned long cons_tx_timeout_cnt; |
||||
}; |
||||
|
||||
struct qtnf_mac_info { |
||||
u8 bands_cap; |
||||
u8 phymode_cap; |
||||
u8 dev_mac[ETH_ALEN]; |
||||
u8 num_tx_chain; |
||||
u8 num_rx_chain; |
||||
u16 max_ap_assoc_sta; |
||||
u32 frag_thr; |
||||
u32 rts_thr; |
||||
u8 lretry_limit; |
||||
u8 sretry_limit; |
||||
u8 coverage_class; |
||||
u8 radar_detect_widths; |
||||
struct ieee80211_ht_cap ht_cap; |
||||
struct ieee80211_vht_cap vht_cap; |
||||
struct ieee80211_iface_limit *limits; |
||||
size_t n_limits; |
||||
}; |
||||
|
||||
struct qtnf_wmac { |
||||
u8 macid; |
||||
u8 wiphy_registered; |
||||
u8 macaddr[ETH_ALEN]; |
||||
struct qtnf_bus *bus; |
||||
struct qtnf_mac_info macinfo; |
||||
struct qtnf_vif iflist[QTNF_MAX_INTF]; |
||||
struct cfg80211_scan_request *scan_req; |
||||
}; |
||||
|
||||
struct qtnf_hw_info { |
||||
u8 num_mac; |
||||
u8 mac_bitmap; |
||||
u8 alpha2_code[QTNF_MAX_ALPHA_LEN]; |
||||
u32 fw_ver; |
||||
u16 ql_proto_ver; |
||||
u8 total_tx_chain; |
||||
u8 total_rx_chain; |
||||
u32 hw_capab; |
||||
}; |
||||
|
||||
struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac); |
||||
struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac); |
||||
struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus); |
||||
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *priv, |
||||
const char *name, unsigned char name_assign_type, |
||||
enum nl80211_iftype iftype); |
||||
void qtnf_main_work_queue(struct work_struct *work); |
||||
int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed); |
||||
int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac); |
||||
|
||||
struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid); |
||||
struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb); |
||||
struct net_device *qtnf_classify_skb_no_mbss(struct qtnf_bus *bus, |
||||
struct sk_buff *skb); |
||||
|
||||
void qtnf_virtual_intf_cleanup(struct net_device *ndev); |
||||
|
||||
void qtnf_netdev_updown(struct net_device *ndev, bool up); |
||||
|
||||
static inline struct qtnf_vif *qtnf_netdev_get_priv(struct net_device *dev) |
||||
{ |
||||
return *((void **)netdev_priv(dev)); |
||||
} |
||||
|
||||
#endif /* _QTN_FMAC_CORE_H_ */ |
@ -0,0 +1,46 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#include "debug.h" |
||||
|
||||
#undef pr_fmt |
||||
#define pr_fmt(fmt) "qtnfmac dbg: %s: " fmt, __func__ |
||||
|
||||
void qtnf_debugfs_init(struct qtnf_bus *bus, const char *name) |
||||
{ |
||||
bus->dbg_dir = debugfs_create_dir(name, NULL); |
||||
|
||||
if (IS_ERR_OR_NULL(bus->dbg_dir)) { |
||||
pr_warn("failed to create debugfs root dir\n"); |
||||
bus->dbg_dir = NULL; |
||||
} |
||||
} |
||||
|
||||
void qtnf_debugfs_remove(struct qtnf_bus *bus) |
||||
{ |
||||
debugfs_remove_recursive(bus->dbg_dir); |
||||
bus->dbg_dir = NULL; |
||||
} |
||||
|
||||
void qtnf_debugfs_add_entry(struct qtnf_bus *bus, const char *name, |
||||
int (*fn)(struct seq_file *seq, void *data)) |
||||
{ |
||||
struct dentry *entry; |
||||
|
||||
entry = debugfs_create_devm_seqfile(bus->dev, name, bus->dbg_dir, fn); |
||||
if (IS_ERR_OR_NULL(entry)) |
||||
pr_warn("failed to add entry (%s)\n", name); |
||||
} |
@ -0,0 +1,50 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_DEBUG_H_ |
||||
#define _QTN_FMAC_DEBUG_H_ |
||||
|
||||
#include <linux/debugfs.h> |
||||
|
||||
#include "core.h" |
||||
#include "bus.h" |
||||
|
||||
#ifdef CONFIG_DEBUG_FS |
||||
|
||||
void qtnf_debugfs_init(struct qtnf_bus *bus, const char *name); |
||||
void qtnf_debugfs_remove(struct qtnf_bus *bus); |
||||
void qtnf_debugfs_add_entry(struct qtnf_bus *bus, const char *name, |
||||
int (*fn)(struct seq_file *seq, void *data)); |
||||
|
||||
#else |
||||
|
||||
static inline void qtnf_debugfs_init(struct qtnf_bus *bus, const char *name) |
||||
{ |
||||
} |
||||
|
||||
static inline void qtnf_debugfs_remove(struct qtnf_bus *bus) |
||||
{ |
||||
} |
||||
|
||||
static inline void |
||||
qtnf_debugfs_add_entry(struct qtnf_bus *bus, const char *name, |
||||
int (*fn)(struct seq_file *seq, void *data)) |
||||
{ |
||||
} |
||||
|
||||
#endif /* CONFIG_DEBUG_FS */ |
||||
|
||||
#endif /* _QTN_FMAC_DEBUG_H_ */ |
@ -0,0 +1,452 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/module.h> |
||||
#include <linux/slab.h> |
||||
|
||||
#include "cfg80211.h" |
||||
#include "core.h" |
||||
#include "qlink.h" |
||||
#include "bus.h" |
||||
#include "trans.h" |
||||
#include "util.h" |
||||
#include "event.h" |
||||
|
||||
static int |
||||
qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif, |
||||
const struct qlink_event_sta_assoc *sta_assoc, |
||||
u16 len) |
||||
{ |
||||
const u8 *sta_addr; |
||||
u16 frame_control; |
||||
struct station_info sinfo = { 0 }; |
||||
size_t payload_len; |
||||
u16 tlv_type; |
||||
u16 tlv_value_len; |
||||
size_t tlv_full_len; |
||||
const struct qlink_tlv_hdr *tlv; |
||||
|
||||
if (unlikely(len < sizeof(*sta_assoc))) { |
||||
pr_err("VIF%u.%u: payload is too short (%u < %zu)\n", |
||||
mac->macid, vif->vifid, len, sizeof(*sta_assoc)); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (vif->wdev.iftype != NL80211_IFTYPE_AP) { |
||||
pr_err("VIF%u.%u: STA_ASSOC event when not in AP mode\n", |
||||
mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
if (!(vif->bss_status & QTNF_STATE_AP_START)) { |
||||
pr_err("VIF%u.%u: STA_ASSOC event when AP is not started\n", |
||||
mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
sta_addr = sta_assoc->sta_addr; |
||||
frame_control = le16_to_cpu(sta_assoc->frame_control); |
||||
|
||||
pr_debug("VIF%u.%u: MAC:%pM FC:%x\n", mac->macid, vif->vifid, sta_addr, |
||||
frame_control); |
||||
|
||||
qtnf_sta_list_add(&vif->sta_list, sta_addr); |
||||
|
||||
sinfo.assoc_req_ies = NULL; |
||||
sinfo.assoc_req_ies_len = 0; |
||||
|
||||
payload_len = len - sizeof(*sta_assoc); |
||||
tlv = (struct qlink_tlv_hdr *)sta_assoc->ies; |
||||
|
||||
while (payload_len >= sizeof(struct qlink_tlv_hdr)) { |
||||
tlv_type = le16_to_cpu(tlv->type); |
||||
tlv_value_len = le16_to_cpu(tlv->len); |
||||
tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); |
||||
|
||||
if (tlv_full_len > payload_len) { |
||||
pr_warn("VIF%u.%u: malformed TLV 0x%.2X; LEN: %u\n", |
||||
mac->macid, vif->vifid, tlv_type, |
||||
tlv_value_len); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (tlv_type == QTN_TLV_ID_IE_SET) { |
||||
sinfo.assoc_req_ies = tlv->val; |
||||
sinfo.assoc_req_ies_len = tlv_value_len; |
||||
} |
||||
|
||||
payload_len -= tlv_full_len; |
||||
tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); |
||||
} |
||||
|
||||
if (payload_len) { |
||||
pr_warn("VIF%u.%u: malformed TLV buf; bytes left: %zu\n", |
||||
mac->macid, vif->vifid, payload_len); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
cfg80211_new_sta(vif->netdev, sta_assoc->sta_addr, &sinfo, |
||||
GFP_KERNEL); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_event_handle_sta_deauth(struct qtnf_wmac *mac, struct qtnf_vif *vif, |
||||
const struct qlink_event_sta_deauth *sta_deauth, |
||||
u16 len) |
||||
{ |
||||
const u8 *sta_addr; |
||||
u16 reason; |
||||
|
||||
if (unlikely(len < sizeof(*sta_deauth))) { |
||||
pr_err("VIF%u.%u: payload is too short (%u < %zu)\n", |
||||
mac->macid, vif->vifid, len, |
||||
sizeof(struct qlink_event_sta_deauth)); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (vif->wdev.iftype != NL80211_IFTYPE_AP) { |
||||
pr_err("VIF%u.%u: STA_DEAUTH event when not in AP mode\n", |
||||
mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
if (!(vif->bss_status & QTNF_STATE_AP_START)) { |
||||
pr_err("VIF%u.%u: STA_DEAUTH event when AP is not started\n", |
||||
mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
sta_addr = sta_deauth->sta_addr; |
||||
reason = le16_to_cpu(sta_deauth->reason); |
||||
|
||||
pr_debug("VIF%u.%u: MAC:%pM reason:%x\n", mac->macid, vif->vifid, |
||||
sta_addr, reason); |
||||
|
||||
if (qtnf_sta_list_del(&vif->sta_list, sta_addr)) |
||||
cfg80211_del_sta(vif->netdev, sta_deauth->sta_addr, |
||||
GFP_KERNEL); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_event_handle_bss_join(struct qtnf_vif *vif, |
||||
const struct qlink_event_bss_join *join_info, |
||||
u16 len) |
||||
{ |
||||
if (unlikely(len < sizeof(*join_info))) { |
||||
pr_err("VIF%u.%u: payload is too short (%u < %zu)\n", |
||||
vif->mac->macid, vif->vifid, len, |
||||
sizeof(struct qlink_event_bss_join)); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { |
||||
pr_err("VIF%u.%u: BSS_JOIN event when not in STA mode\n", |
||||
vif->mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
if (vif->sta_state != QTNF_STA_CONNECTING) { |
||||
pr_err("VIF%u.%u: BSS_JOIN event when STA is not connecting\n", |
||||
vif->mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
pr_debug("VIF%u.%u: BSSID:%pM\n", vif->mac->macid, vif->vifid, |
||||
join_info->bssid); |
||||
|
||||
cfg80211_connect_result(vif->netdev, join_info->bssid, NULL, 0, NULL, |
||||
0, le16_to_cpu(join_info->status), GFP_KERNEL); |
||||
|
||||
if (le16_to_cpu(join_info->status) == WLAN_STATUS_SUCCESS) { |
||||
vif->sta_state = QTNF_STA_CONNECTED; |
||||
netif_carrier_on(vif->netdev); |
||||
} else { |
||||
vif->sta_state = QTNF_STA_DISCONNECTED; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_event_handle_bss_leave(struct qtnf_vif *vif, |
||||
const struct qlink_event_bss_leave *leave_info, |
||||
u16 len) |
||||
{ |
||||
if (unlikely(len < sizeof(*leave_info))) { |
||||
pr_err("VIF%u.%u: payload is too short (%u < %zu)\n", |
||||
vif->mac->macid, vif->vifid, len, |
||||
sizeof(struct qlink_event_bss_leave)); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { |
||||
pr_err("VIF%u.%u: BSS_LEAVE event when not in STA mode\n", |
||||
vif->mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
if (vif->sta_state != QTNF_STA_CONNECTED) { |
||||
pr_err("VIF%u.%u: BSS_LEAVE event when STA is not connected\n", |
||||
vif->mac->macid, vif->vifid); |
||||
return -EPROTO; |
||||
} |
||||
|
||||
pr_debug("VIF%u.%u: disconnected\n", vif->mac->macid, vif->vifid); |
||||
|
||||
cfg80211_disconnected(vif->netdev, leave_info->reason, NULL, 0, 0, |
||||
GFP_KERNEL); |
||||
|
||||
vif->sta_state = QTNF_STA_DISCONNECTED; |
||||
netif_carrier_off(vif->netdev); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_event_handle_mgmt_received(struct qtnf_vif *vif, |
||||
const struct qlink_event_rxmgmt *rxmgmt, |
||||
u16 len) |
||||
{ |
||||
const size_t min_len = sizeof(*rxmgmt) + |
||||
sizeof(struct ieee80211_hdr_3addr); |
||||
const struct ieee80211_hdr_3addr *frame = (void *)rxmgmt->frame_data; |
||||
const u16 frame_len = len - sizeof(*rxmgmt); |
||||
enum nl80211_rxmgmt_flags flags = 0; |
||||
|
||||
if (unlikely(len < min_len)) { |
||||
pr_err("VIF%u.%u: payload is too short (%u < %zu)\n", |
||||
vif->mac->macid, vif->vifid, len, min_len); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (le32_to_cpu(rxmgmt->flags) & QLINK_RXMGMT_FLAG_ANSWERED) |
||||
flags |= NL80211_RXMGMT_FLAG_ANSWERED; |
||||
|
||||
pr_debug("%s LEN:%u FC:%.4X SA:%pM\n", vif->netdev->name, frame_len, |
||||
le16_to_cpu(frame->frame_control), frame->addr2); |
||||
|
||||
cfg80211_rx_mgmt(&vif->wdev, le32_to_cpu(rxmgmt->freq), |
||||
le32_to_cpu(rxmgmt->sig_dbm), rxmgmt->frame_data, |
||||
frame_len, flags); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_event_handle_scan_results(struct qtnf_vif *vif, |
||||
const struct qlink_event_scan_result *sr, |
||||
u16 len) |
||||
{ |
||||
struct cfg80211_bss *bss; |
||||
struct ieee80211_channel *channel; |
||||
struct wiphy *wiphy = priv_to_wiphy(vif->mac); |
||||
enum cfg80211_bss_frame_type frame_type; |
||||
size_t payload_len; |
||||
u16 tlv_type; |
||||
u16 tlv_value_len; |
||||
size_t tlv_full_len; |
||||
const struct qlink_tlv_hdr *tlv; |
||||
|
||||
const u8 *ies = NULL; |
||||
size_t ies_len = 0; |
||||
|
||||
if (len < sizeof(*sr)) { |
||||
pr_err("VIF%u.%u: payload is too short\n", vif->mac->macid, |
||||
vif->vifid); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
channel = ieee80211_get_channel(wiphy, le16_to_cpu(sr->freq)); |
||||
if (!channel) { |
||||
pr_err("VIF%u.%u: channel at %u MHz not found\n", |
||||
vif->mac->macid, vif->vifid, le16_to_cpu(sr->freq)); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
switch (sr->frame_type) { |
||||
case QLINK_BSS_FTYPE_BEACON: |
||||
frame_type = CFG80211_BSS_FTYPE_BEACON; |
||||
break; |
||||
case QLINK_BSS_FTYPE_PRESP: |
||||
frame_type = CFG80211_BSS_FTYPE_PRESP; |
||||
break; |
||||
default: |
||||
frame_type = CFG80211_BSS_FTYPE_UNKNOWN; |
||||
} |
||||
|
||||
payload_len = len - sizeof(*sr); |
||||
tlv = (struct qlink_tlv_hdr *)sr->payload; |
||||
|
||||
while (payload_len >= sizeof(struct qlink_tlv_hdr)) { |
||||
tlv_type = le16_to_cpu(tlv->type); |
||||
tlv_value_len = le16_to_cpu(tlv->len); |
||||
tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); |
||||
|
||||
if (tlv_full_len > payload_len) { |
||||
pr_warn("VIF%u.%u: malformed TLV 0x%.2X; LEN: %u\n", |
||||
vif->mac->macid, vif->vifid, tlv_type, |
||||
tlv_value_len); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (tlv_type == QTN_TLV_ID_IE_SET) { |
||||
ies = tlv->val; |
||||
ies_len = tlv_value_len; |
||||
} |
||||
|
||||
payload_len -= tlv_full_len; |
||||
tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); |
||||
} |
||||
|
||||
if (payload_len) { |
||||
pr_warn("VIF%u.%u: malformed TLV buf; bytes left: %zu\n", |
||||
vif->mac->macid, vif->vifid, payload_len); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
bss = cfg80211_inform_bss(wiphy, channel, frame_type, |
||||
sr->bssid, get_unaligned_le64(&sr->tsf), |
||||
le16_to_cpu(sr->capab), |
||||
le16_to_cpu(sr->bintval), ies, ies_len, |
||||
sr->signal, GFP_KERNEL); |
||||
if (!bss) |
||||
return -ENOMEM; |
||||
|
||||
cfg80211_put_bss(wiphy, bss); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int |
||||
qtnf_event_handle_scan_complete(struct qtnf_wmac *mac, |
||||
const struct qlink_event_scan_complete *status, |
||||
u16 len) |
||||
{ |
||||
if (len < sizeof(*status)) { |
||||
pr_err("MAC%u: payload is too short\n", mac->macid); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
qtnf_scan_done(mac, le32_to_cpu(status->flags) & QLINK_SCAN_ABORTED); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int qtnf_event_parse(struct qtnf_wmac *mac, |
||||
const struct sk_buff *event_skb) |
||||
{ |
||||
const struct qlink_event *event; |
||||
struct qtnf_vif *vif = NULL; |
||||
int ret = -1; |
||||
u16 event_id; |
||||
u16 event_len; |
||||
|
||||
event = (const struct qlink_event *)event_skb->data; |
||||
event_id = le16_to_cpu(event->event_id); |
||||
event_len = le16_to_cpu(event->mhdr.len); |
||||
|
||||
if (likely(event->vifid < QTNF_MAX_INTF)) { |
||||
vif = &mac->iflist[event->vifid]; |
||||
} else { |
||||
pr_err("invalid vif(%u)\n", event->vifid); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
switch (event_id) { |
||||
case QLINK_EVENT_STA_ASSOCIATED: |
||||
ret = qtnf_event_handle_sta_assoc(mac, vif, (const void *)event, |
||||
event_len); |
||||
break; |
||||
case QLINK_EVENT_STA_DEAUTH: |
||||
ret = qtnf_event_handle_sta_deauth(mac, vif, |
||||
(const void *)event, |
||||
event_len); |
||||
break; |
||||
case QLINK_EVENT_MGMT_RECEIVED: |
||||
ret = qtnf_event_handle_mgmt_received(vif, (const void *)event, |
||||
event_len); |
||||
break; |
||||
case QLINK_EVENT_SCAN_RESULTS: |
||||
ret = qtnf_event_handle_scan_results(vif, (const void *)event, |
||||
event_len); |
||||
break; |
||||
case QLINK_EVENT_SCAN_COMPLETE: |
||||
ret = qtnf_event_handle_scan_complete(mac, (const void *)event, |
||||
event_len); |
||||
break; |
||||
case QLINK_EVENT_BSS_JOIN: |
||||
ret = qtnf_event_handle_bss_join(vif, (const void *)event, |
||||
event_len); |
||||
break; |
||||
case QLINK_EVENT_BSS_LEAVE: |
||||
ret = qtnf_event_handle_bss_leave(vif, (const void *)event, |
||||
event_len); |
||||
break; |
||||
default: |
||||
pr_warn("unknown event type: %x\n", event_id); |
||||
break; |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int qtnf_event_process_skb(struct qtnf_bus *bus, |
||||
const struct sk_buff *skb) |
||||
{ |
||||
const struct qlink_event *event; |
||||
struct qtnf_wmac *mac; |
||||
int res; |
||||
|
||||
if (unlikely(!skb || skb->len < sizeof(*event))) { |
||||
pr_err("invalid event buffer\n"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
event = (struct qlink_event *)skb->data; |
||||
|
||||
mac = qtnf_core_get_mac(bus, event->macid); |
||||
|
||||
pr_debug("new event id:%x len:%u mac:%u vif:%u\n", |
||||
le16_to_cpu(event->event_id), le16_to_cpu(event->mhdr.len), |
||||
event->macid, event->vifid); |
||||
|
||||
if (unlikely(!mac)) |
||||
return -ENXIO; |
||||
|
||||
qtnf_bus_lock(bus); |
||||
res = qtnf_event_parse(mac, skb); |
||||
qtnf_bus_unlock(bus); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
void qtnf_event_work_handler(struct work_struct *work) |
||||
{ |
||||
struct qtnf_bus *bus = container_of(work, struct qtnf_bus, event_work); |
||||
struct sk_buff_head *event_queue = &bus->trans.event_queue; |
||||
struct sk_buff *current_event_skb = skb_dequeue(event_queue); |
||||
|
||||
while (current_event_skb) { |
||||
qtnf_event_process_skb(bus, current_event_skb); |
||||
dev_kfree_skb_any(current_event_skb); |
||||
current_event_skb = skb_dequeue(event_queue); |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_EVENT_H_ |
||||
#define _QTN_FMAC_EVENT_H_ |
||||
|
||||
#include <linux/kernel.h> |
||||
#include <linux/module.h> |
||||
|
||||
#include "qlink.h" |
||||
|
||||
void qtnf_event_work_handler(struct work_struct *work); |
||||
|
||||
#endif /* _QTN_FMAC_EVENT_H_ */ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,89 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_PCIE_H_ |
||||
#define _QTN_FMAC_PCIE_H_ |
||||
|
||||
#include <linux/dma-mapping.h> |
||||
#include <linux/io.h> |
||||
|
||||
#include "pcie_regs_pearl.h" |
||||
#include "pcie_ipc.h" |
||||
#include "shm_ipc.h" |
||||
|
||||
struct bus; |
||||
|
||||
struct qtnf_pcie_bus_priv { |
||||
struct pci_dev *pdev; |
||||
|
||||
/* lock for irq configuration changes */ |
||||
spinlock_t irq_lock; |
||||
|
||||
/* lock for tx operations */ |
||||
spinlock_t tx_lock; |
||||
u8 msi_enabled; |
||||
int mps; |
||||
|
||||
struct workqueue_struct *workqueue; |
||||
struct tasklet_struct reclaim_tq; |
||||
|
||||
void __iomem *sysctl_bar; |
||||
void __iomem *epmem_bar; |
||||
void __iomem *dmareg_bar; |
||||
|
||||
struct qtnf_shm_ipc shm_ipc_ep_in; |
||||
struct qtnf_shm_ipc shm_ipc_ep_out; |
||||
|
||||
struct qtnf_pcie_bda __iomem *bda; |
||||
void __iomem *pcie_reg_base; |
||||
|
||||
u16 tx_bd_num; |
||||
u16 rx_bd_num; |
||||
|
||||
struct sk_buff **tx_skb; |
||||
struct sk_buff **rx_skb; |
||||
|
||||
struct qtnf_tx_bd *tx_bd_vbase; |
||||
dma_addr_t tx_bd_pbase; |
||||
|
||||
struct qtnf_rx_bd *rx_bd_vbase; |
||||
dma_addr_t rx_bd_pbase; |
||||
|
||||
dma_addr_t bd_table_paddr; |
||||
void *bd_table_vaddr; |
||||
u32 bd_table_len; |
||||
|
||||
u32 hw_txproc_wr_ptr; |
||||
|
||||
u16 tx_bd_reclaim_start; |
||||
u16 tx_bd_index; |
||||
u32 tx_queue_len; |
||||
|
||||
u16 rx_bd_index; |
||||
|
||||
u32 pcie_irq_mask; |
||||
|
||||
/* diagnostics stats */ |
||||
u32 pcie_irq_count; |
||||
u32 pcie_irq_rx_count; |
||||
u32 pcie_irq_tx_count; |
||||
u32 tx_full_count; |
||||
u32 tx_done_count; |
||||
u32 tx_reclaim_done; |
||||
u32 tx_reclaim_req; |
||||
}; |
||||
|
||||
#endif /* _QTN_FMAC_PCIE_H_ */ |
@ -0,0 +1,158 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_PCIE_IPC_H_ |
||||
#define _QTN_FMAC_PCIE_IPC_H_ |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
#include "shm_ipc_defs.h" |
||||
|
||||
/* bitmap for EP status and flags: updated by EP, read by RC */ |
||||
#define QTN_EP_HAS_UBOOT BIT(0) |
||||
#define QTN_EP_HAS_FIRMWARE BIT(1) |
||||
#define QTN_EP_REQ_UBOOT BIT(2) |
||||
#define QTN_EP_REQ_FIRMWARE BIT(3) |
||||
#define QTN_EP_ERROR_UBOOT BIT(4) |
||||
#define QTN_EP_ERROR_FIRMWARE BIT(5) |
||||
|
||||
#define QTN_EP_FW_LOADRDY BIT(8) |
||||
#define QTN_EP_FW_SYNC BIT(9) |
||||
#define QTN_EP_FW_RETRY BIT(10) |
||||
#define QTN_EP_FW_QLINK_DONE BIT(15) |
||||
#define QTN_EP_FW_DONE BIT(16) |
||||
|
||||
/* bitmap for RC status and flags: updated by RC, read by EP */ |
||||
#define QTN_RC_PCIE_LINK BIT(0) |
||||
#define QTN_RC_NET_LINK BIT(1) |
||||
#define QTN_RC_FW_FLASHBOOT BIT(5) |
||||
#define QTN_RC_FW_QLINK BIT(7) |
||||
#define QTN_RC_FW_LOADRDY BIT(8) |
||||
#define QTN_RC_FW_SYNC BIT(9) |
||||
|
||||
/* state transition timeouts */ |
||||
#define QTN_FW_DL_TIMEOUT_MS 3000 |
||||
#define QTN_FW_QLINK_TIMEOUT_MS 30000 |
||||
|
||||
#define PCIE_HDP_INT_RX_BITS (0 \ |
||||
| PCIE_HDP_INT_EP_TXDMA \
|
||||
| PCIE_HDP_INT_EP_TXEMPTY \
|
||||
) |
||||
|
||||
#define PCIE_HDP_INT_TX_BITS (0 \ |
||||
| PCIE_HDP_INT_EP_RXDMA \
|
||||
) |
||||
|
||||
#if BITS_PER_LONG == 64 |
||||
#define QTN_HOST_HI32(a) ((u32)(((u64)a) >> 32)) |
||||
#define QTN_HOST_LO32(a) ((u32)(((u64)a) & 0xffffffffUL)) |
||||
#define QTN_HOST_ADDR(h, l) ((((u64)h) << 32) | ((u64)l)) |
||||
#elif BITS_PER_LONG == 32 |
||||
#define QTN_HOST_HI32(a) 0 |
||||
#define QTN_HOST_LO32(a) ((u32)(((u32)a) & 0xffffffffUL)) |
||||
#define QTN_HOST_ADDR(h, l) ((u32)l) |
||||
#else |
||||
#error Unexpected BITS_PER_LONG value |
||||
#endif |
||||
|
||||
#define QTN_SYSCTL_BAR 0 |
||||
#define QTN_SHMEM_BAR 2 |
||||
#define QTN_DMA_BAR 3 |
||||
|
||||
#define QTN_PCIE_BDA_VERSION 0x1002 |
||||
|
||||
#define PCIE_BDA_NAMELEN 32 |
||||
#define PCIE_HHBM_MAX_SIZE 512 |
||||
|
||||
#define SKB_BUF_SIZE 2048 |
||||
|
||||
#define QTN_PCIE_BOARDFLG "PCIEQTN" |
||||
#define QTN_PCIE_FW_DLMASK 0xF |
||||
#define QTN_PCIE_FW_BUFSZ 2048 |
||||
|
||||
#define QTN_ENET_ADDR_LENGTH 6 |
||||
|
||||
#define QTN_TXDONE_MASK ((u32)0x80000000) |
||||
#define QTN_GET_LEN(x) ((x) & 0xFFFF) |
||||
|
||||
#define QTN_PCIE_TX_DESC_LEN_MASK 0xFFFF |
||||
#define QTN_PCIE_TX_DESC_LEN_SHIFT 0 |
||||
#define QTN_PCIE_TX_DESC_PORT_MASK 0xF |
||||
#define QTN_PCIE_TX_DESC_PORT_SHIFT 16 |
||||
#define QTN_PCIE_TX_DESC_TQE_BIT BIT(24) |
||||
|
||||
#define QTN_EP_LHOST_TQE_PORT 4 |
||||
|
||||
enum qtnf_pcie_bda_ipc_flags { |
||||
QTN_PCIE_IPC_FLAG_HBM_MAGIC = BIT(0), |
||||
QTN_PCIE_IPC_FLAG_SHM_PIO = BIT(1), |
||||
}; |
||||
|
||||
struct qtnf_pcie_bda { |
||||
__le16 bda_len; |
||||
__le16 bda_version; |
||||
__le32 bda_pci_endian; |
||||
__le32 bda_ep_state; |
||||
__le32 bda_rc_state; |
||||
__le32 bda_dma_mask; |
||||
__le32 bda_msi_addr; |
||||
__le32 bda_flashsz; |
||||
u8 bda_boardname[PCIE_BDA_NAMELEN]; |
||||
__le32 bda_rc_msi_enabled; |
||||
__le32 bda_hhbm_list[PCIE_HHBM_MAX_SIZE]; |
||||
__le32 bda_dsbw_start_index; |
||||
__le32 bda_dsbw_end_index; |
||||
__le32 bda_dsbw_total_bytes; |
||||
__le32 bda_rc_tx_bd_base; |
||||
__le32 bda_rc_tx_bd_num; |
||||
u8 bda_pcie_mac[QTN_ENET_ADDR_LENGTH]; |
||||
struct qtnf_shm_ipc_region bda_shm_reg1 __aligned(4096); /* host TX */ |
||||
struct qtnf_shm_ipc_region bda_shm_reg2 __aligned(4096); /* host RX */ |
||||
} __packed; |
||||
|
||||
struct qtnf_tx_bd { |
||||
__le32 addr; |
||||
__le32 addr_h; |
||||
__le32 info; |
||||
__le32 info_h; |
||||
} __packed; |
||||
|
||||
struct qtnf_rx_bd { |
||||
__le32 addr; |
||||
__le32 addr_h; |
||||
__le32 info; |
||||
__le32 info_h; |
||||
__le32 next_ptr; |
||||
__le32 next_ptr_h; |
||||
} __packed; |
||||
|
||||
enum qtnf_fw_loadtype { |
||||
QTN_FW_DBEGIN, |
||||
QTN_FW_DSUB, |
||||
QTN_FW_DEND, |
||||
QTN_FW_CTRL |
||||
}; |
||||
|
||||
struct qtnf_pcie_fw_hdr { |
||||
u8 boardflg[8]; |
||||
__le32 fwsize; |
||||
__le32 seqnum; |
||||
__le32 type; |
||||
__le32 pktlen; |
||||
__le32 crc; |
||||
} __packed; |
||||
|
||||
#endif /* _QTN_FMAC_PCIE_IPC_H_ */ |
@ -0,0 +1,353 @@ |
||||
/*
|
||||
* Copyright (c) 2015 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __PEARL_PCIE_H |
||||
#define __PEARL_PCIE_H |
||||
|
||||
#define PCIE_GEN2_BASE (0xe9000000) |
||||
#define PCIE_GEN3_BASE (0xe7000000) |
||||
|
||||
#define PEARL_CUR_PCIE_BASE (PCIE_GEN2_BASE) |
||||
#define PCIE_HDP_OFFSET (0x2000) |
||||
|
||||
#define PCIE_HDP_CTRL(base) ((base) + 0x2c00) |
||||
#define PCIE_HDP_AXI_CTRL(base) ((base) + 0x2c04) |
||||
#define PCIE_HDP_HOST_WR_DESC0(base) ((base) + 0x2c10) |
||||
#define PCIE_HDP_HOST_WR_DESC0_H(base) ((base) + 0x2c14) |
||||
#define PCIE_HDP_HOST_WR_DESC1(base) ((base) + 0x2c18) |
||||
#define PCIE_HDP_HOST_WR_DESC1_H(base) ((base) + 0x2c1c) |
||||
#define PCIE_HDP_HOST_WR_DESC2(base) ((base) + 0x2c20) |
||||
#define PCIE_HDP_HOST_WR_DESC2_H(base) ((base) + 0x2c24) |
||||
#define PCIE_HDP_HOST_WR_DESC3(base) ((base) + 0x2c28) |
||||
#define PCIE_HDP_HOST_WR_DESC4_H(base) ((base) + 0x2c2c) |
||||
#define PCIE_HDP_RX_INT_CTRL(base) ((base) + 0x2c30) |
||||
#define PCIE_HDP_TX_INT_CTRL(base) ((base) + 0x2c34) |
||||
#define PCIE_HDP_INT_STATUS(base) ((base) + 0x2c38) |
||||
#define PCIE_HDP_INT_EN(base) ((base) + 0x2c3c) |
||||
#define PCIE_HDP_RX_DESC0_PTR(base) ((base) + 0x2c40) |
||||
#define PCIE_HDP_RX_DESC0_NOE(base) ((base) + 0x2c44) |
||||
#define PCIE_HDP_RX_DESC1_PTR(base) ((base) + 0x2c48) |
||||
#define PCIE_HDP_RX_DESC1_NOE(base) ((base) + 0x2c4c) |
||||
#define PCIE_HDP_RX_DESC2_PTR(base) ((base) + 0x2c50) |
||||
#define PCIE_HDP_RX_DESC2_NOE(base) ((base) + 0x2c54) |
||||
#define PCIE_HDP_RX_DESC3_PTR(base) ((base) + 0x2c58) |
||||
#define PCIE_HDP_RX_DESC3_NOE(base) ((base) + 0x2c5c) |
||||
|
||||
#define PCIE_HDP_TX0_BASE_ADDR(base) ((base) + 0x2c60) |
||||
#define PCIE_HDP_TX1_BASE_ADDR(base) ((base) + 0x2c64) |
||||
#define PCIE_HDP_TX0_Q_CTRL(base) ((base) + 0x2c70) |
||||
#define PCIE_HDP_TX1_Q_CTRL(base) ((base) + 0x2c74) |
||||
#define PCIE_HDP_CFG0(base) ((base) + 0x2c80) |
||||
#define PCIE_HDP_CFG1(base) ((base) + 0x2c84) |
||||
#define PCIE_HDP_CFG2(base) ((base) + 0x2c88) |
||||
#define PCIE_HDP_CFG3(base) ((base) + 0x2c8c) |
||||
#define PCIE_HDP_CFG4(base) ((base) + 0x2c90) |
||||
#define PCIE_HDP_CFG5(base) ((base) + 0x2c94) |
||||
#define PCIE_HDP_CFG6(base) ((base) + 0x2c98) |
||||
#define PCIE_HDP_CFG7(base) ((base) + 0x2c9c) |
||||
#define PCIE_HDP_CFG8(base) ((base) + 0x2ca0) |
||||
#define PCIE_HDP_CFG9(base) ((base) + 0x2ca4) |
||||
#define PCIE_HDP_CFG10(base) ((base) + 0x2ca8) |
||||
#define PCIE_HDP_CFG11(base) ((base) + 0x2cac) |
||||
#define PCIE_INT(base) ((base) + 0x2cb0) |
||||
#define PCIE_INT_MASK(base) ((base) + 0x2cb4) |
||||
#define PCIE_MSI_MASK(base) ((base) + 0x2cb8) |
||||
#define PCIE_MSI_PNDG(base) ((base) + 0x2cbc) |
||||
#define PCIE_PRI_CFG(base) ((base) + 0x2cc0) |
||||
#define PCIE_PHY_CR(base) ((base) + 0x2cc4) |
||||
#define PCIE_HDP_CTAG_CTRL(base) ((base) + 0x2cf4) |
||||
#define PCIE_HDP_HHBM_BUF_PTR(base) ((base) + 0x2d00) |
||||
#define PCIE_HDP_HHBM_BUF_PTR_H(base) ((base) + 0x2d04) |
||||
#define PCIE_HDP_HHBM_BUF_FIFO_NOE(base) ((base) + 0x2d04) |
||||
#define PCIE_HDP_RX0DMA_CNT(base) ((base) + 0x2d10) |
||||
#define PCIE_HDP_RX1DMA_CNT(base) ((base) + 0x2d14) |
||||
#define PCIE_HDP_RX2DMA_CNT(base) ((base) + 0x2d18) |
||||
#define PCIE_HDP_RX3DMA_CNT(base) ((base) + 0x2d1c) |
||||
#define PCIE_HDP_TX0DMA_CNT(base) ((base) + 0x2d20) |
||||
#define PCIE_HDP_TX1DMA_CNT(base) ((base) + 0x2d24) |
||||
#define PCIE_HDP_RXDMA_CTRL(base) ((base) + 0x2d28) |
||||
#define PCIE_HDP_TX_HOST_Q_SZ_CTRL(base) ((base) + 0x2d2c) |
||||
#define PCIE_HDP_TX_HOST_Q_BASE_L(base) ((base) + 0x2d30) |
||||
#define PCIE_HDP_TX_HOST_Q_BASE_H(base) ((base) + 0x2d34) |
||||
#define PCIE_HDP_TX_HOST_Q_WR_PTR(base) ((base) + 0x2d38) |
||||
#define PCIE_HDP_TX_HOST_Q_RD_PTR(base) ((base) + 0x2d3c) |
||||
#define PCIE_HDP_TX_HOST_Q_STS(base) ((base) + 0x2d40) |
||||
|
||||
/* Host HBM pool registers */ |
||||
#define PCIE_HHBM_CSR_REG(base) ((base) + 0x2e00) |
||||
#define PCIE_HHBM_Q_BASE_REG(base) ((base) + 0x2e04) |
||||
#define PCIE_HHBM_Q_LIMIT_REG(base) ((base) + 0x2e08) |
||||
#define PCIE_HHBM_Q_WR_REG(base) ((base) + 0x2e0c) |
||||
#define PCIE_HHBM_Q_RD_REG(base) ((base) + 0x2e10) |
||||
#define PCIE_HHBM_POOL_DATA_0_H(base) ((base) + 0x2e90) |
||||
#define PCIE_HHBM_CONFIG(base) ((base) + 0x2f9c) |
||||
#define PCIE_HHBM_POOL_REQ_0(base) ((base) + 0x2f10) |
||||
#define PCIE_HHBM_POOL_DATA_0(base) ((base) + 0x2f40) |
||||
#define PCIE_HHBM_WATERMARK_MASKED_INT(base) ((base) + 0x2f68) |
||||
#define PCIE_HHBM_WATERMARK_INT(base) ((base) + 0x2f6c) |
||||
#define PCIE_HHBM_POOL_WATERMARK(base) ((base) + 0x2f70) |
||||
#define PCIE_HHBM_POOL_OVERFLOW_CNT(base) ((base) + 0x2f90) |
||||
#define PCIE_HHBM_POOL_UNDERFLOW_CNT(base) ((base) + 0x2f94) |
||||
#define HBM_INT_STATUS(base) ((base) + 0x2f9c) |
||||
#define PCIE_HHBM_POOL_CNFIG(base) ((base) + 0x2f9c) |
||||
|
||||
/* host HBM bit field definition */ |
||||
#define HHBM_CONFIG_SOFT_RESET (BIT(8)) |
||||
#define HHBM_WR_REQ (BIT(0)) |
||||
#define HHBM_RD_REQ (BIT(1)) |
||||
#define HHBM_DONE (BIT(31)) |
||||
|
||||
/* offsets for dual PCIE */ |
||||
#define PCIE_PORT_LINK_CTL(base) ((base) + 0x0710) |
||||
#define PCIE_GEN2_CTL(base) ((base) + 0x080C) |
||||
#define PCIE_GEN3_OFF(base) ((base) + 0x0890) |
||||
#define PCIE_ATU_CTRL1(base) ((base) + 0x0904) |
||||
#define PCIE_ATU_CTRL2(base) ((base) + 0x0908) |
||||
#define PCIE_ATU_BASE_LOW(base) ((base) + 0x090C) |
||||
#define PCIE_ATU_BASE_HIGH(base) ((base) + 0x0910) |
||||
#define PCIE_ATU_BASE_LIMIT(base) ((base) + 0x0914) |
||||
#define PCIE_ATU_TGT_LOW(base) ((base) + 0x0918) |
||||
#define PCIE_ATU_TGT_HIGH(base) ((base) + 0x091C) |
||||
#define PCIE_DMA_WR_ENABLE(base) ((base) + 0x097C) |
||||
#define PCIE_DMA_WR_CHWTLOW(base) ((base) + 0x0988) |
||||
#define PCIE_DMA_WR_CHWTHIG(base) ((base) + 0x098C) |
||||
#define PCIE_DMA_WR_INTSTS(base) ((base) + 0x09BC) |
||||
#define PCIE_DMA_WR_INTMASK(base) ((base) + 0x09C4) |
||||
#define PCIE_DMA_WR_INTCLER(base) ((base) + 0x09C8) |
||||
#define PCIE_DMA_WR_DONE_IMWR_ADDR_L(base) ((base) + 0x09D0) |
||||
#define PCIE_DMA_WR_DONE_IMWR_ADDR_H(base) ((base) + 0x09D4) |
||||
#define PCIE_DMA_WR_ABORT_IMWR_ADDR_L(base) ((base) + 0x09D8) |
||||
#define PCIE_DMA_WR_ABORT_IMWR_ADDR_H(base) ((base) + 0x09DC) |
||||
#define PCIE_DMA_WR_IMWR_DATA(base) ((base) + 0x09E0) |
||||
#define PCIE_DMA_WR_LL_ERR_EN(base) ((base) + 0x0A00) |
||||
#define PCIE_DMA_WR_DOORBELL(base) ((base) + 0x0980) |
||||
#define PCIE_DMA_RD_ENABLE(base) ((base) + 0x099C) |
||||
#define PCIE_DMA_RD_DOORBELL(base) ((base) + 0x09A0) |
||||
#define PCIE_DMA_RD_CHWTLOW(base) ((base) + 0x09A8) |
||||
#define PCIE_DMA_RD_CHWTHIG(base) ((base) + 0x09AC) |
||||
#define PCIE_DMA_RD_INTSTS(base) ((base) + 0x0A10) |
||||
#define PCIE_DMA_RD_INTMASK(base) ((base) + 0x0A18) |
||||
#define PCIE_DMA_RD_INTCLER(base) ((base) + 0x0A1C) |
||||
#define PCIE_DMA_RD_ERR_STS_L(base) ((base) + 0x0A24) |
||||
#define PCIE_DMA_RD_ERR_STS_H(base) ((base) + 0x0A28) |
||||
#define PCIE_DMA_RD_LL_ERR_EN(base) ((base) + 0x0A34) |
||||
#define PCIE_DMA_RD_DONE_IMWR_ADDR_L(base) ((base) + 0x0A3C) |
||||
#define PCIE_DMA_RD_DONE_IMWR_ADDR_H(base) ((base) + 0x0A40) |
||||
#define PCIE_DMA_RD_ABORT_IMWR_ADDR_L(base) ((base) + 0x0A44) |
||||
#define PCIE_DMA_RD_ABORT_IMWR_ADDR_H(base) ((base) + 0x0A48) |
||||
#define PCIE_DMA_RD_IMWR_DATA(base) ((base) + 0x0A4C) |
||||
#define PCIE_DMA_CHNL_CONTEXT(base) ((base) + 0x0A6C) |
||||
#define PCIE_DMA_CHNL_CNTRL(base) ((base) + 0x0A70) |
||||
#define PCIE_DMA_XFR_SIZE(base) ((base) + 0x0A78) |
||||
#define PCIE_DMA_SAR_LOW(base) ((base) + 0x0A7C) |
||||
#define PCIE_DMA_SAR_HIGH(base) ((base) + 0x0A80) |
||||
#define PCIE_DMA_DAR_LOW(base) ((base) + 0x0A84) |
||||
#define PCIE_DMA_DAR_HIGH(base) ((base) + 0x0A88) |
||||
#define PCIE_DMA_LLPTR_LOW(base) ((base) + 0x0A8C) |
||||
#define PCIE_DMA_LLPTR_HIGH(base) ((base) + 0x0A90) |
||||
#define PCIE_DMA_WRLL_ERR_ENB(base) ((base) + 0x0A00) |
||||
#define PCIE_DMA_RDLL_ERR_ENB(base) ((base) + 0x0A34) |
||||
#define PCIE_DMABD_CHNL_CNTRL(base) ((base) + 0x8000) |
||||
#define PCIE_DMABD_XFR_SIZE(base) ((base) + 0x8004) |
||||
#define PCIE_DMABD_SAR_LOW(base) ((base) + 0x8008) |
||||
#define PCIE_DMABD_SAR_HIGH(base) ((base) + 0x800c) |
||||
#define PCIE_DMABD_DAR_LOW(base) ((base) + 0x8010) |
||||
#define PCIE_DMABD_DAR_HIGH(base) ((base) + 0x8014) |
||||
#define PCIE_DMABD_LLPTR_LOW(base) ((base) + 0x8018) |
||||
#define PCIE_DMABD_LLPTR_HIGH(base) ((base) + 0x801c) |
||||
#define PCIE_WRDMA0_CHNL_CNTRL(base) ((base) + 0x8000) |
||||
#define PCIE_WRDMA0_XFR_SIZE(base) ((base) + 0x8004) |
||||
#define PCIE_WRDMA0_SAR_LOW(base) ((base) + 0x8008) |
||||
#define PCIE_WRDMA0_SAR_HIGH(base) ((base) + 0x800c) |
||||
#define PCIE_WRDMA0_DAR_LOW(base) ((base) + 0x8010) |
||||
#define PCIE_WRDMA0_DAR_HIGH(base) ((base) + 0x8014) |
||||
#define PCIE_WRDMA0_LLPTR_LOW(base) ((base) + 0x8018) |
||||
#define PCIE_WRDMA0_LLPTR_HIGH(base) ((base) + 0x801c) |
||||
#define PCIE_WRDMA1_CHNL_CNTRL(base) ((base) + 0x8020) |
||||
#define PCIE_WRDMA1_XFR_SIZE(base) ((base) + 0x8024) |
||||
#define PCIE_WRDMA1_SAR_LOW(base) ((base) + 0x8028) |
||||
#define PCIE_WRDMA1_SAR_HIGH(base) ((base) + 0x802c) |
||||
#define PCIE_WRDMA1_DAR_LOW(base) ((base) + 0x8030) |
||||
#define PCIE_WRDMA1_DAR_HIGH(base) ((base) + 0x8034) |
||||
#define PCIE_WRDMA1_LLPTR_LOW(base) ((base) + 0x8038) |
||||
#define PCIE_WRDMA1_LLPTR_HIGH(base) ((base) + 0x803c) |
||||
#define PCIE_RDDMA0_CHNL_CNTRL(base) ((base) + 0x8040) |
||||
#define PCIE_RDDMA0_XFR_SIZE(base) ((base) + 0x8044) |
||||
#define PCIE_RDDMA0_SAR_LOW(base) ((base) + 0x8048) |
||||
#define PCIE_RDDMA0_SAR_HIGH(base) ((base) + 0x804c) |
||||
#define PCIE_RDDMA0_DAR_LOW(base) ((base) + 0x8050) |
||||
#define PCIE_RDDMA0_DAR_HIGH(base) ((base) + 0x8054) |
||||
#define PCIE_RDDMA0_LLPTR_LOW(base) ((base) + 0x8058) |
||||
#define PCIE_RDDMA0_LLPTR_HIGH(base) ((base) + 0x805c) |
||||
#define PCIE_RDDMA1_CHNL_CNTRL(base) ((base) + 0x8060) |
||||
#define PCIE_RDDMA1_XFR_SIZE(base) ((base) + 0x8064) |
||||
#define PCIE_RDDMA1_SAR_LOW(base) ((base) + 0x8068) |
||||
#define PCIE_RDDMA1_SAR_HIGH(base) ((base) + 0x806c) |
||||
#define PCIE_RDDMA1_DAR_LOW(base) ((base) + 0x8070) |
||||
#define PCIE_RDDMA1_DAR_HIGH(base) ((base) + 0x8074) |
||||
#define PCIE_RDDMA1_LLPTR_LOW(base) ((base) + 0x8078) |
||||
#define PCIE_RDDMA1_LLPTR_HIGH(base) ((base) + 0x807c) |
||||
|
||||
#define PCIE_ID(base) ((base) + 0x0000) |
||||
#define PCIE_CMD(base) ((base) + 0x0004) |
||||
#define PCIE_BAR(base, n) ((base) + 0x0010 + ((n) << 2)) |
||||
#define PCIE_CAP_PTR(base) ((base) + 0x0034) |
||||
#define PCIE_MSI_LBAR(base) ((base) + 0x0054) |
||||
#define PCIE_MSI_CTRL(base) ((base) + 0x0050) |
||||
#define PCIE_MSI_ADDR_L(base) ((base) + 0x0054) |
||||
#define PCIE_MSI_ADDR_H(base) ((base) + 0x0058) |
||||
#define PCIE_MSI_DATA(base) ((base) + 0x005C) |
||||
#define PCIE_MSI_MASK_BIT(base) ((base) + 0x0060) |
||||
#define PCIE_MSI_PEND_BIT(base) ((base) + 0x0064) |
||||
#define PCIE_DEVCAP(base) ((base) + 0x0074) |
||||
#define PCIE_DEVCTLSTS(base) ((base) + 0x0078) |
||||
|
||||
#define PCIE_CMDSTS(base) ((base) + 0x0004) |
||||
#define PCIE_LINK_STAT(base) ((base) + 0x80) |
||||
#define PCIE_LINK_CTL2(base) ((base) + 0xa0) |
||||
#define PCIE_ASPM_L1_CTRL(base) ((base) + 0x70c) |
||||
#define PCIE_ASPM_LINK_CTRL(base) (PCIE_LINK_STAT) |
||||
#define PCIE_ASPM_L1_SUBSTATE_TIMING(base) ((base) + 0xB44) |
||||
#define PCIE_L1SUB_CTRL1(base) ((base) + 0x150) |
||||
#define PCIE_PMCSR(base) ((base) + 0x44) |
||||
#define PCIE_CFG_SPACE_LIMIT(base) ((base) + 0x100) |
||||
|
||||
/* PCIe link defines */ |
||||
#define PEARL_PCIE_LINKUP (0x7) |
||||
#define PEARL_PCIE_DATA_LINK (BIT(0)) |
||||
#define PEARL_PCIE_PHY_LINK (BIT(1)) |
||||
#define PEARL_PCIE_LINK_RST (BIT(3)) |
||||
#define PEARL_PCIE_FATAL_ERR (BIT(5)) |
||||
#define PEARL_PCIE_NONFATAL_ERR (BIT(6)) |
||||
|
||||
/* PCIe Lane defines */ |
||||
#define PCIE_G2_LANE_X1 ((BIT(0)) << 16) |
||||
#define PCIE_G2_LANE_X2 ((BIT(0) | BIT(1)) << 16) |
||||
|
||||
/* PCIe DLL link enable */ |
||||
#define PCIE_DLL_LINK_EN ((BIT(0)) << 5) |
||||
|
||||
#define PCIE_LINK_GEN1 (BIT(0)) |
||||
#define PCIE_LINK_GEN2 (BIT(1)) |
||||
#define PCIE_LINK_GEN3 (BIT(2)) |
||||
#define PCIE_LINK_MODE(x) (((x) >> 16) & 0x7) |
||||
|
||||
#define MSI_EN (BIT(0)) |
||||
#define MSI_64_EN (BIT(7)) |
||||
#define PCIE_MSI_ADDR_OFFSET(a) ((a) & 0xFFFF) |
||||
#define PCIE_MSI_ADDR_ALIGN(a) ((a) & (~0xFFFF)) |
||||
|
||||
#define PCIE_BAR_MASK(base, n) ((base) + 0x1010 + ((n) << 2)) |
||||
#define PCIE_MAX_BAR (6) |
||||
|
||||
#define PCIE_ATU_VIEW(base) ((base) + 0x0900) |
||||
#define PCIE_ATU_CTL1(base) ((base) + 0x0904) |
||||
#define PCIE_ATU_CTL2(base) ((base) + 0x0908) |
||||
#define PCIE_ATU_LBAR(base) ((base) + 0x090c) |
||||
#define PCIE_ATU_UBAR(base) ((base) + 0x0910) |
||||
#define PCIE_ATU_LAR(base) ((base) + 0x0914) |
||||
#define PCIE_ATU_LTAR(base) ((base) + 0x0918) |
||||
#define PCIE_ATU_UTAR(base) ((base) + 0x091c) |
||||
|
||||
#define PCIE_MSI_ADDR_LOWER(base) ((base) + 0x0820) |
||||
#define PCIE_MSI_ADDR_UPPER(base) ((base) + 0x0824) |
||||
#define PCIE_MSI_ENABLE(base) ((base) + 0x0828) |
||||
#define PCIE_MSI_MASK_RC(base) ((base) + 0x082c) |
||||
#define PCIE_MSI_STATUS(base) ((base) + 0x0830) |
||||
#define PEARL_PCIE_MSI_REGION (0xce000000) |
||||
#define PEARL_PCIE_MSI_DATA (0) |
||||
#define PCIE_MSI_GPIO(base) ((base) + 0x0888) |
||||
|
||||
#define PCIE_HDP_HOST_QUEUE_FULL (BIT(17)) |
||||
#define USE_BAR_MATCH_MODE |
||||
#define PCIE_ATU_OB_REGION (BIT(0)) |
||||
#define PCIE_ATU_EN_REGION (BIT(31)) |
||||
#define PCIE_ATU_EN_MATCH (BIT(30)) |
||||
#define PCIE_BASE_REGION (0xb0000000) |
||||
#define PCIE_MEM_MAP_SIZE (512 * 1024) |
||||
|
||||
#define PCIE_OB_REG_REGION (0xcf000000) |
||||
#define PCIE_CONFIG_REGION (0xcf000000) |
||||
#define PCIE_CONFIG_SIZE (4096) |
||||
#define PCIE_CONFIG_CH (1) |
||||
|
||||
/* inbound mapping */ |
||||
#define PCIE_IB_BAR0 (0x00000000) /* ddr */ |
||||
#define PCIE_IB_BAR0_CH (0) |
||||
#define PCIE_IB_BAR3 (0xe0000000) /* sys_reg */ |
||||
#define PCIE_IB_BAR3_CH (1) |
||||
|
||||
/* outbound mapping */ |
||||
#define PCIE_MEM_CH (0) |
||||
#define PCIE_REG_CH (1) |
||||
#define PCIE_MEM_REGION (0xc0000000) |
||||
#define PCIE_MEM_SIZE (0x000fffff) |
||||
#define PCIE_MEM_TAR (0x80000000) |
||||
|
||||
#define PCIE_MSI_REGION (0xce000000) |
||||
#define PCIE_MSI_SIZE (KBYTE(4) - 1) |
||||
#define PCIE_MSI_CH (1) |
||||
|
||||
/* size of config region */ |
||||
#define PCIE_CFG_SIZE (0x0000ffff) |
||||
|
||||
#define PCIE_ATU_DIR_IB (BIT(31)) |
||||
#define PCIE_ATU_DIR_OB (0) |
||||
#define PCIE_ATU_DIR_CFG (2) |
||||
#define PCIE_ATU_DIR_MATCH_IB (BIT(31) | BIT(30)) |
||||
|
||||
#define PCIE_DMA_WR_0 (0) |
||||
#define PCIE_DMA_WR_1 (1) |
||||
#define PCIE_DMA_RD_0 (2) |
||||
#define PCIE_DMA_RD_1 (3) |
||||
|
||||
#define PCIE_DMA_CHNL_CNTRL_CB (BIT(0)) |
||||
#define PCIE_DMA_CHNL_CNTRL_TCB (BIT(1)) |
||||
#define PCIE_DMA_CHNL_CNTRL_LLP (BIT(2)) |
||||
#define PCIE_DMA_CHNL_CNTRL_LIE (BIT(3)) |
||||
#define PCIE_DMA_CHNL_CNTRL_RIE (BIT(4)) |
||||
#define PCIE_DMA_CHNL_CNTRL_CSS (BIT(8)) |
||||
#define PCIE_DMA_CHNL_CNTRL_LLE (BIT(9)) |
||||
#define PCIE_DMA_CHNL_CNTRL_TLP (BIT(26)) |
||||
|
||||
#define PCIE_DMA_CHNL_CONTEXT_RD (BIT(31)) |
||||
#define PCIE_DMA_CHNL_CONTEXT_WR (0) |
||||
#define PCIE_MAX_BAR (6) |
||||
|
||||
/* PCIe HDP interrupt status definition */ |
||||
#define PCIE_HDP_INT_EP_RXDMA (BIT(0)) |
||||
#define PCIE_HDP_INT_HBM_UF (BIT(1)) |
||||
#define PCIE_HDP_INT_RX_LEN_ERR (BIT(2)) |
||||
#define PCIE_HDP_INT_RX_HDR_LEN_ERR (BIT(3)) |
||||
#define PCIE_HDP_INT_EP_TXDMA (BIT(12)) |
||||
#define PCIE_HDP_INT_EP_TXEMPTY (BIT(15)) |
||||
#define PCIE_HDP_INT_IPC (BIT(29)) |
||||
|
||||
/* PCIe interrupt status definition */ |
||||
#define PCIE_INT_MSI (BIT(24)) |
||||
#define PCIE_INT_INTX (BIT(23)) |
||||
|
||||
/* PCIe legacy INTx */ |
||||
#define PEARL_PCIE_CFG0_OFFSET (0x6C) |
||||
#define PEARL_ASSERT_INTX (BIT(9)) |
||||
|
||||
/* SYS CTL regs */ |
||||
#define QTN_PEARL_SYSCTL_LHOST_IRQ_OFFSET (0x001C) |
||||
|
||||
#define QTN_PEARL_IPC_IRQ_WORD(irq) (BIT(irq) | BIT(irq + 16)) |
||||
#define QTN_PEARL_LHOST_IPC_IRQ (6) |
||||
|
||||
#endif /* __PEARL_PCIE_H */ |
@ -0,0 +1,901 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_QLINK_H_ |
||||
#define _QTN_QLINK_H_ |
||||
|
||||
#include <linux/ieee80211.h> |
||||
|
||||
#define QLINK_PROTO_VER 3 |
||||
|
||||
#define QLINK_MACID_RSVD 0xFF |
||||
#define QLINK_VIFID_RSVD 0xFF |
||||
|
||||
/* Common QLINK protocol messages definitions.
|
||||
*/ |
||||
|
||||
/**
|
||||
* enum qlink_msg_type - QLINK message types |
||||
* |
||||
* Used to distinguish between message types of QLINK protocol. |
||||
* |
||||
* @QLINK_MSG_TYPE_CMD: Message is carrying data of a command sent from |
||||
* driver to wireless hardware. |
||||
* @QLINK_MSG_TYPE_CMDRSP: Message is carrying data of a response to a command. |
||||
* Sent from wireless HW to driver in reply to previously issued command. |
||||
* @QLINK_MSG_TYPE_EVENT: Data for an event originated in wireless hardware and |
||||
* sent asynchronously to driver. |
||||
*/ |
||||
enum qlink_msg_type { |
||||
QLINK_MSG_TYPE_CMD = 1, |
||||
QLINK_MSG_TYPE_CMDRSP = 2, |
||||
QLINK_MSG_TYPE_EVENT = 3 |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_msg_header - common QLINK protocol message header |
||||
* |
||||
* Portion of QLINK protocol header common for all message types. |
||||
* |
||||
* @type: Message type, one of &enum qlink_msg_type. |
||||
* @len: Total length of message including all headers. |
||||
*/ |
||||
struct qlink_msg_header { |
||||
__le16 type; |
||||
__le16 len; |
||||
} __packed; |
||||
|
||||
/* Generic definitions of data and information carried in QLINK messages
|
||||
*/ |
||||
|
||||
enum qlink_hw_capab { |
||||
QLINK_HW_SUPPORTS_REG_UPDATE = BIT(0), |
||||
}; |
||||
|
||||
enum qlink_phy_mode { |
||||
QLINK_PHYMODE_BGN = BIT(0), |
||||
QLINK_PHYMODE_AN = BIT(1), |
||||
QLINK_PHYMODE_AC = BIT(2), |
||||
}; |
||||
|
||||
enum qlink_iface_type { |
||||
QLINK_IFTYPE_AP = 1, |
||||
QLINK_IFTYPE_STATION = 2, |
||||
QLINK_IFTYPE_ADHOC = 3, |
||||
QLINK_IFTYPE_MONITOR = 4, |
||||
QLINK_IFTYPE_WDS = 5, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_intf_info - information on virtual interface. |
||||
* |
||||
* Data describing a single virtual interface. |
||||
* |
||||
* @if_type: Mode of interface operation, one of &enum qlink_iface_type |
||||
* @flags: interface flagsmap. |
||||
* @mac_addr: MAC address of virtual interface. |
||||
*/ |
||||
struct qlink_intf_info { |
||||
__le16 if_type; |
||||
__le16 flags; |
||||
u8 mac_addr[ETH_ALEN]; |
||||
u8 rsvd[2]; |
||||
} __packed; |
||||
|
||||
enum qlink_sta_flags { |
||||
QLINK_STA_FLAG_INVALID = 0, |
||||
QLINK_STA_FLAG_AUTHORIZED = BIT(0), |
||||
QLINK_STA_FLAG_SHORT_PREAMBLE = BIT(1), |
||||
QLINK_STA_FLAG_WME = BIT(2), |
||||
QLINK_STA_FLAG_MFP = BIT(3), |
||||
QLINK_STA_FLAG_AUTHENTICATED = BIT(4), |
||||
QLINK_STA_FLAG_TDLS_PEER = BIT(5), |
||||
QLINK_STA_FLAG_ASSOCIATED = BIT(6), |
||||
}; |
||||
|
||||
enum qlink_channel_width { |
||||
QLINK_CHAN_WIDTH_5 = BIT(0), |
||||
QLINK_CHAN_WIDTH_10 = BIT(1), |
||||
QLINK_CHAN_WIDTH_20_NOHT = BIT(2), |
||||
QLINK_CHAN_WIDTH_20 = BIT(3), |
||||
QLINK_CHAN_WIDTH_40 = BIT(4), |
||||
QLINK_CHAN_WIDTH_80 = BIT(5), |
||||
QLINK_CHAN_WIDTH_80P80 = BIT(6), |
||||
QLINK_CHAN_WIDTH_160 = BIT(7), |
||||
}; |
||||
|
||||
/* QLINK Command messages related definitions
|
||||
*/ |
||||
|
||||
/**
|
||||
* enum qlink_cmd_type - list of supported commands |
||||
* |
||||
* Commands are QLINK messages of type @QLINK_MSG_TYPE_CMD, sent by driver to |
||||
* wireless network device for processing. Device is expected to send back a |
||||
* reply message of type &QLINK_MSG_TYPE_CMDRSP, containing at least command |
||||
* execution status (one of &enum qlink_cmd_result) at least. Reply message |
||||
* may also contain data payload specific to the command type. |
||||
* |
||||
* @QLINK_CMD_CHANS_INFO_GET: for the specified MAC and specified band, get |
||||
* number of operational channels and information on each of the channel. |
||||
* This command is generic to a specified MAC, interface index must be set |
||||
* to QLINK_VIFID_RSVD in command header. |
||||
*/ |
||||
enum qlink_cmd_type { |
||||
QLINK_CMD_FW_INIT = 0x0001, |
||||
QLINK_CMD_FW_DEINIT = 0x0002, |
||||
QLINK_CMD_REGISTER_MGMT = 0x0003, |
||||
QLINK_CMD_SEND_MGMT_FRAME = 0x0004, |
||||
QLINK_CMD_MGMT_SET_APPIE = 0x0005, |
||||
QLINK_CMD_PHY_PARAMS_GET = 0x0011, |
||||
QLINK_CMD_PHY_PARAMS_SET = 0x0012, |
||||
QLINK_CMD_GET_HW_INFO = 0x0013, |
||||
QLINK_CMD_MAC_INFO = 0x0014, |
||||
QLINK_CMD_ADD_INTF = 0x0015, |
||||
QLINK_CMD_DEL_INTF = 0x0016, |
||||
QLINK_CMD_CHANGE_INTF = 0x0017, |
||||
QLINK_CMD_UPDOWN_INTF = 0x0018, |
||||
QLINK_CMD_REG_REGION = 0x0019, |
||||
QLINK_CMD_CHANS_INFO_GET = 0x001A, |
||||
QLINK_CMD_CONFIG_AP = 0x0020, |
||||
QLINK_CMD_START_AP = 0x0021, |
||||
QLINK_CMD_STOP_AP = 0x0022, |
||||
QLINK_CMD_GET_STA_INFO = 0x0030, |
||||
QLINK_CMD_ADD_KEY = 0x0040, |
||||
QLINK_CMD_DEL_KEY = 0x0041, |
||||
QLINK_CMD_SET_DEFAULT_KEY = 0x0042, |
||||
QLINK_CMD_SET_DEFAULT_MGMT_KEY = 0x0043, |
||||
QLINK_CMD_CHANGE_STA = 0x0051, |
||||
QLINK_CMD_DEL_STA = 0x0052, |
||||
QLINK_CMD_SCAN = 0x0053, |
||||
QLINK_CMD_CONNECT = 0x0060, |
||||
QLINK_CMD_DISCONNECT = 0x0061, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_cmd - QLINK command message header |
||||
* |
||||
* Header used for QLINK messages of QLINK_MSG_TYPE_CMD type. |
||||
* |
||||
* @mhdr: Common QLINK message header. |
||||
* @cmd_id: command id, one of &enum qlink_cmd_type. |
||||
* @seq_num: sequence number of command message, used for matching with |
||||
* response message. |
||||
* @macid: index of physical radio device the command is destined to or |
||||
* QLINK_MACID_RSVD if not applicable. |
||||
* @vifid: index of virtual wireless interface on specified @macid the command |
||||
* is destined to or QLINK_VIFID_RSVD if not applicable. |
||||
*/ |
||||
struct qlink_cmd { |
||||
struct qlink_msg_header mhdr; |
||||
__le16 cmd_id; |
||||
__le16 seq_num; |
||||
u8 rsvd[2]; |
||||
u8 macid; |
||||
u8 vifid; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_manage_intf - interface management command |
||||
* |
||||
* Data for interface management commands QLINK_CMD_ADD_INTF, QLINK_CMD_DEL_INTF |
||||
* and QLINK_CMD_CHANGE_INTF. |
||||
* |
||||
* @intf_info: interface description. |
||||
*/ |
||||
struct qlink_cmd_manage_intf { |
||||
struct qlink_cmd chdr; |
||||
struct qlink_intf_info intf_info; |
||||
} __packed; |
||||
|
||||
enum qlink_mgmt_frame_type { |
||||
QLINK_MGMT_FRAME_ASSOC_REQ = 0x00, |
||||
QLINK_MGMT_FRAME_ASSOC_RESP = 0x01, |
||||
QLINK_MGMT_FRAME_REASSOC_REQ = 0x02, |
||||
QLINK_MGMT_FRAME_REASSOC_RESP = 0x03, |
||||
QLINK_MGMT_FRAME_PROBE_REQ = 0x04, |
||||
QLINK_MGMT_FRAME_PROBE_RESP = 0x05, |
||||
QLINK_MGMT_FRAME_BEACON = 0x06, |
||||
QLINK_MGMT_FRAME_ATIM = 0x07, |
||||
QLINK_MGMT_FRAME_DISASSOC = 0x08, |
||||
QLINK_MGMT_FRAME_AUTH = 0x09, |
||||
QLINK_MGMT_FRAME_DEAUTH = 0x0A, |
||||
QLINK_MGMT_FRAME_ACTION = 0x0B, |
||||
|
||||
QLINK_MGMT_FRAME_TYPE_COUNT |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_mgmt_frame_register - data for QLINK_CMD_REGISTER_MGMT |
||||
* |
||||
* @frame_type: MGMT frame type the registration request describes, one of |
||||
* &enum qlink_mgmt_frame_type. |
||||
* @do_register: 0 - unregister, otherwise register for reception of specified |
||||
* MGMT frame type. |
||||
*/ |
||||
struct qlink_cmd_mgmt_frame_register { |
||||
struct qlink_cmd chdr; |
||||
__le16 frame_type; |
||||
u8 do_register; |
||||
} __packed; |
||||
|
||||
enum qlink_mgmt_frame_tx_flags { |
||||
QLINK_MGMT_FRAME_TX_FLAG_NONE = 0, |
||||
QLINK_MGMT_FRAME_TX_FLAG_OFFCHAN = BIT(0), |
||||
QLINK_MGMT_FRAME_TX_FLAG_NO_CCK = BIT(1), |
||||
QLINK_MGMT_FRAME_TX_FLAG_ACK_NOWAIT = BIT(2), |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_mgmt_frame_tx - data for QLINK_CMD_SEND_MGMT_FRAME command |
||||
* |
||||
* @cookie: opaque request identifier. |
||||
* @freq: Frequency to use for frame transmission. |
||||
* @flags: Transmission flags, one of &enum qlink_mgmt_frame_tx_flags. |
||||
* @frame_data: frame to transmit. |
||||
*/ |
||||
struct qlink_cmd_mgmt_frame_tx { |
||||
struct qlink_cmd chdr; |
||||
__le32 cookie; |
||||
__le16 freq; |
||||
__le16 flags; |
||||
u8 frame_data[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_mgmt_append_ie - data for QLINK_CMD_MGMT_SET_APPIE command |
||||
* |
||||
* @type: type of MGMT frame to appent requested IEs to, one of |
||||
* &enum qlink_mgmt_frame_type. |
||||
* @flags: for future use. |
||||
* @ie_data: IEs data to append. |
||||
*/ |
||||
struct qlink_cmd_mgmt_append_ie { |
||||
struct qlink_cmd chdr; |
||||
u8 type; |
||||
u8 flags; |
||||
u8 ie_data[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_get_sta_info - data for QLINK_CMD_GET_STA_INFO command |
||||
* |
||||
* @sta_addr: MAC address of the STA statistics is requested for. |
||||
*/ |
||||
struct qlink_cmd_get_sta_info { |
||||
struct qlink_cmd chdr; |
||||
u8 sta_addr[ETH_ALEN]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_add_key - data for QLINK_CMD_ADD_KEY command. |
||||
* |
||||
* @key_index: index of the key being installed. |
||||
* @pairwise: whether to use pairwise key. |
||||
* @addr: MAC address of a STA key is being installed to. |
||||
* @cipher: cipher suite. |
||||
* @key_data: key data itself. |
||||
*/ |
||||
struct qlink_cmd_add_key { |
||||
struct qlink_cmd chdr; |
||||
u8 key_index; |
||||
u8 pairwise; |
||||
u8 addr[ETH_ALEN]; |
||||
__le32 cipher; |
||||
u8 key_data[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_del_key_req - data for QLINK_CMD_DEL_KEY command |
||||
* |
||||
* @key_index: index of the key being removed. |
||||
* @pairwise: whether to use pairwise key. |
||||
* @addr: MAC address of a STA for which a key is removed. |
||||
*/ |
||||
struct qlink_cmd_del_key { |
||||
struct qlink_cmd chdr; |
||||
u8 key_index; |
||||
u8 pairwise; |
||||
u8 addr[ETH_ALEN]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_set_def_key - data for QLINK_CMD_SET_DEFAULT_KEY command |
||||
* |
||||
* @key_index: index of the key to be set as default one. |
||||
* @unicast: key is unicast. |
||||
* @multicast: key is multicast. |
||||
*/ |
||||
struct qlink_cmd_set_def_key { |
||||
struct qlink_cmd chdr; |
||||
u8 key_index; |
||||
u8 unicast; |
||||
u8 multicast; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_set_def_mgmt_key - data for QLINK_CMD_SET_DEFAULT_MGMT_KEY |
||||
* |
||||
* @key_index: index of the key to be set as default MGMT key. |
||||
*/ |
||||
struct qlink_cmd_set_def_mgmt_key { |
||||
struct qlink_cmd chdr; |
||||
u8 key_index; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_change_sta - data for QLINK_CMD_CHANGE_STA command |
||||
* |
||||
* @sta_flags_mask: STA flags mask, bitmap of &enum qlink_sta_flags |
||||
* @sta_flags_set: STA flags values, bitmap of &enum qlink_sta_flags |
||||
* @sta_addr: address of the STA for which parameters are set. |
||||
*/ |
||||
struct qlink_cmd_change_sta { |
||||
struct qlink_cmd chdr; |
||||
__le32 sta_flags_mask; |
||||
__le32 sta_flags_set; |
||||
u8 sta_addr[ETH_ALEN]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_del_sta - data for QLINK_CMD_DEL_STA command. |
||||
* |
||||
* See &struct station_del_parameters |
||||
*/ |
||||
struct qlink_cmd_del_sta { |
||||
struct qlink_cmd chdr; |
||||
__le16 reason_code; |
||||
u8 subtype; |
||||
u8 sta_addr[ETH_ALEN]; |
||||
} __packed; |
||||
|
||||
enum qlink_sta_connect_flags { |
||||
QLINK_STA_CONNECT_DISABLE_HT = BIT(0), |
||||
QLINK_STA_CONNECT_DISABLE_VHT = BIT(1), |
||||
QLINK_STA_CONNECT_USE_RRM = BIT(2), |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_connect - data for QLINK_CMD_CONNECT command |
||||
* |
||||
* @flags: for future use. |
||||
* @freq: center frequence of a channel which should be used to connect. |
||||
* @bg_scan_period: period of background scan. |
||||
* @bssid: BSSID of the BSS to connect to. |
||||
* @payload: variable portion of connection request. |
||||
*/ |
||||
struct qlink_cmd_connect { |
||||
struct qlink_cmd chdr; |
||||
__le32 flags; |
||||
__le16 freq; |
||||
__le16 bg_scan_period; |
||||
u8 bssid[ETH_ALEN]; |
||||
u8 payload[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_disconnect - data for QLINK_CMD_DISCONNECT command |
||||
* |
||||
* @reason: code of the reason of disconnect, see &enum ieee80211_reasoncode. |
||||
*/ |
||||
struct qlink_cmd_disconnect { |
||||
struct qlink_cmd chdr; |
||||
__le16 reason; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_updown - data for QLINK_CMD_UPDOWN_INTF command |
||||
* |
||||
* @if_up: bring specified interface DOWN (if_up==0) or UP (otherwise). |
||||
* Interface is specified in common command header @chdr. |
||||
*/ |
||||
struct qlink_cmd_updown { |
||||
struct qlink_cmd chdr; |
||||
u8 if_up; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* enum qlink_band - a list of frequency bands |
||||
* |
||||
* @QLINK_BAND_2GHZ: 2.4GHz band |
||||
* @QLINK_BAND_5GHZ: 5GHz band |
||||
* @QLINK_BAND_60GHZ: 60GHz band |
||||
*/ |
||||
enum qlink_band { |
||||
QLINK_BAND_2GHZ = BIT(0), |
||||
QLINK_BAND_5GHZ = BIT(1), |
||||
QLINK_BAND_60GHZ = BIT(2), |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_cmd_chans_info_get - data for QLINK_CMD_CHANS_INFO_GET command |
||||
* |
||||
* @band: a PHY band for which channels info is needed, one of @enum qlink_band |
||||
*/ |
||||
struct qlink_cmd_chans_info_get { |
||||
struct qlink_cmd chdr; |
||||
u8 band; |
||||
} __packed; |
||||
|
||||
/* QLINK Command Responses messages related definitions
|
||||
*/ |
||||
|
||||
enum qlink_cmd_result { |
||||
QLINK_CMD_RESULT_OK = 0, |
||||
QLINK_CMD_RESULT_INVALID, |
||||
QLINK_CMD_RESULT_ENOTSUPP, |
||||
QLINK_CMD_RESULT_ENOTFOUND, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_resp - QLINK command response message header |
||||
* |
||||
* Header used for QLINK messages of QLINK_MSG_TYPE_CMDRSP type. |
||||
* |
||||
* @mhdr: see &struct qlink_msg_header. |
||||
* @cmd_id: command ID the response corresponds to, one of &enum qlink_cmd_type. |
||||
* @seq_num: sequence number of command message, used for matching with |
||||
* response message. |
||||
* @result: result of the command execution, one of &enum qlink_cmd_result. |
||||
* @macid: index of physical radio device the response is sent from or |
||||
* QLINK_MACID_RSVD if not applicable. |
||||
* @vifid: index of virtual wireless interface on specified @macid the response |
||||
* is sent from or QLINK_VIFID_RSVD if not applicable. |
||||
*/ |
||||
struct qlink_resp { |
||||
struct qlink_msg_header mhdr; |
||||
__le16 cmd_id; |
||||
__le16 seq_num; |
||||
__le16 result; |
||||
u8 macid; |
||||
u8 vifid; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_resp_get_mac_info - response for QLINK_CMD_MAC_INFO command |
||||
* |
||||
* Data describing specific physical device providing wireless MAC |
||||
* functionality. |
||||
* |
||||
* @dev_mac: MAC address of physical WMAC device (used for first BSS on |
||||
* specified WMAC). |
||||
* @num_tx_chain: Number of transmit chains used by WMAC. |
||||
* @num_rx_chain: Number of receive chains used by WMAC. |
||||
* @vht_cap: VHT capabilities. |
||||
* @ht_cap: HT capabilities. |
||||
* @bands_cap: wireless bands WMAC can operate in, bitmap of &enum qlink_band. |
||||
* @phymode_cap: PHY modes WMAC can operate in, bitmap of &enum qlink_phy_mode. |
||||
* @max_ap_assoc_sta: Maximum number of associations supported by WMAC. |
||||
* @radar_detect_widths: bitmask of channels BW for which WMAC can detect radar. |
||||
* @var_info: variable-length WMAC info data. |
||||
*/ |
||||
struct qlink_resp_get_mac_info { |
||||
struct qlink_resp rhdr; |
||||
u8 dev_mac[ETH_ALEN]; |
||||
u8 num_tx_chain; |
||||
u8 num_rx_chain; |
||||
struct ieee80211_vht_cap vht_cap; |
||||
struct ieee80211_ht_cap ht_cap; |
||||
u8 bands_cap; |
||||
u8 phymode_cap; |
||||
__le16 max_ap_assoc_sta; |
||||
__le16 radar_detect_widths; |
||||
u8 var_info[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_resp_get_hw_info - response for QLINK_CMD_GET_HW_INFO command |
||||
* |
||||
* Description of wireless hardware capabilities and features. |
||||
* |
||||
* @fw_ver: wireless hardware firmware version. |
||||
* @hw_capab: Bitmap of capabilities supported by firmware. |
||||
* @ql_proto_ver: Version of QLINK protocol used by firmware. |
||||
* @country_code: country code ID firmware is configured to. |
||||
* @num_mac: Number of separate physical radio devices provided by hardware. |
||||
* @mac_bitmap: Bitmap of MAC IDs that are active and can be used in firmware. |
||||
* @total_tx_chains: total number of transmit chains used by device. |
||||
* @total_rx_chains: total number of receive chains. |
||||
*/ |
||||
struct qlink_resp_get_hw_info { |
||||
struct qlink_resp rhdr; |
||||
__le32 fw_ver; |
||||
__le32 hw_capab; |
||||
__le16 ql_proto_ver; |
||||
u8 alpha2_code[2]; |
||||
u8 num_mac; |
||||
u8 mac_bitmap; |
||||
u8 total_tx_chain; |
||||
u8 total_rx_chain; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_resp_manage_intf - response for interface management commands |
||||
* |
||||
* Response data for QLINK_CMD_ADD_INTF and QLINK_CMD_CHANGE_INTF commands. |
||||
* |
||||
* @rhdr: Common Command Response message header. |
||||
* @intf_info: interface description. |
||||
*/ |
||||
struct qlink_resp_manage_intf { |
||||
struct qlink_resp rhdr; |
||||
struct qlink_intf_info intf_info; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_resp_get_sta_info - response for QLINK_CMD_GET_STA_INFO command |
||||
* |
||||
* Response data containing statistics for specified STA. |
||||
* |
||||
* @sta_addr: MAC address of STA the response carries statistic for. |
||||
* @info: statistics for specified STA. |
||||
*/ |
||||
struct qlink_resp_get_sta_info { |
||||
struct qlink_resp rhdr; |
||||
u8 sta_addr[ETH_ALEN]; |
||||
u8 info[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_resp_get_chan_info - response for QLINK_CMD_CHANS_INFO_GET cmd |
||||
* |
||||
* @band: frequency band to which channels belong to, one of @enum qlink_band. |
||||
* @num_chans: total number of channels info data contained in reply data. |
||||
* @info: variable-length channels info. |
||||
*/ |
||||
struct qlink_resp_get_chan_info { |
||||
struct qlink_resp rhdr; |
||||
u8 band; |
||||
u8 num_chans; |
||||
u8 rsvd[2]; |
||||
u8 info[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_resp_phy_params - response for QLINK_CMD_PHY_PARAMS_GET command |
||||
* |
||||
* @info: variable-length array of PHY params. |
||||
*/ |
||||
struct qlink_resp_phy_params { |
||||
struct qlink_resp rhdr; |
||||
u8 info[0]; |
||||
} __packed; |
||||
|
||||
/* QLINK Events messages related definitions
|
||||
*/ |
||||
|
||||
enum qlink_event_type { |
||||
QLINK_EVENT_STA_ASSOCIATED = 0x0021, |
||||
QLINK_EVENT_STA_DEAUTH = 0x0022, |
||||
QLINK_EVENT_MGMT_RECEIVED = 0x0023, |
||||
QLINK_EVENT_SCAN_RESULTS = 0x0024, |
||||
QLINK_EVENT_SCAN_COMPLETE = 0x0025, |
||||
QLINK_EVENT_BSS_JOIN = 0x0026, |
||||
QLINK_EVENT_BSS_LEAVE = 0x0027, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_event - QLINK event message header |
||||
* |
||||
* Header used for QLINK messages of QLINK_MSG_TYPE_EVENT type. |
||||
* |
||||
* @mhdr: Common QLINK message header. |
||||
* @event_id: Specifies specific event ID, one of &enum qlink_event_type. |
||||
* @macid: index of physical radio device the event was generated on or |
||||
* QLINK_MACID_RSVD if not applicable. |
||||
* @vifid: index of virtual wireless interface on specified @macid the event |
||||
* was generated on or QLINK_VIFID_RSVD if not applicable. |
||||
*/ |
||||
struct qlink_event { |
||||
struct qlink_msg_header mhdr; |
||||
__le16 event_id; |
||||
u8 macid; |
||||
u8 vifid; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_event_sta_assoc - data for QLINK_EVENT_STA_ASSOCIATED event |
||||
* |
||||
* @sta_addr: Address of a STA for which new association event was generated |
||||
* @frame_control: control bits from 802.11 ASSOC_REQUEST header. |
||||
* @payload: IEs from association request. |
||||
*/ |
||||
struct qlink_event_sta_assoc { |
||||
struct qlink_event ehdr; |
||||
u8 sta_addr[ETH_ALEN]; |
||||
__le16 frame_control; |
||||
u8 ies[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_event_sta_deauth - data for QLINK_EVENT_STA_DEAUTH event |
||||
* |
||||
* @sta_addr: Address of a deauthenticated STA. |
||||
* @reason: reason for deauthentication. |
||||
*/ |
||||
struct qlink_event_sta_deauth { |
||||
struct qlink_event ehdr; |
||||
u8 sta_addr[ETH_ALEN]; |
||||
__le16 reason; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_event_bss_join - data for QLINK_EVENT_BSS_JOIN event |
||||
* |
||||
* @bssid: BSSID of a BSS which interface tried to joined. |
||||
* @status: status of joining attempt, see &enum ieee80211_statuscode. |
||||
*/ |
||||
struct qlink_event_bss_join { |
||||
struct qlink_event ehdr; |
||||
u8 bssid[ETH_ALEN]; |
||||
__le16 status; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* struct qlink_event_bss_leave - data for QLINK_EVENT_BSS_LEAVE event |
||||
* |
||||
* @reason: reason of disconnecting from BSS. |
||||
*/ |
||||
struct qlink_event_bss_leave { |
||||
struct qlink_event ehdr; |
||||
u16 reason; |
||||
} __packed; |
||||
|
||||
enum qlink_rxmgmt_flags { |
||||
QLINK_RXMGMT_FLAG_ANSWERED = 1 << 0, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_event_rxmgmt - data for QLINK_EVENT_MGMT_RECEIVED event |
||||
* |
||||
* @freq: Frequency on which the frame was received in MHz. |
||||
* @sig_dbm: signal strength in dBm. |
||||
* @flags: bitmap of &enum qlink_rxmgmt_flags. |
||||
* @frame_data: data of Rx'd frame itself. |
||||
*/ |
||||
struct qlink_event_rxmgmt { |
||||
struct qlink_event ehdr; |
||||
__le32 freq; |
||||
__le32 sig_dbm; |
||||
__le32 flags; |
||||
u8 frame_data[0]; |
||||
} __packed; |
||||
|
||||
enum qlink_frame_type { |
||||
QLINK_BSS_FTYPE_UNKNOWN, |
||||
QLINK_BSS_FTYPE_BEACON, |
||||
QLINK_BSS_FTYPE_PRESP, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_event_scan_result - data for QLINK_EVENT_SCAN_RESULTS event |
||||
* |
||||
* @tsf: TSF timestamp indicating when scan results were generated. |
||||
* @freq: Center frequency of the channel where BSS for which the scan result |
||||
* event was generated was discovered. |
||||
* @capab: capabilities field. |
||||
* @bintval: beacon interval announced by discovered BSS. |
||||
* @signal: signal strength. |
||||
* @frame_type: frame type used to get scan result, see &enum qlink_frame_type. |
||||
* @bssid: BSSID announced by discovered BSS. |
||||
* @ssid_len: length of SSID announced by BSS. |
||||
* @ssid: SSID announced by discovered BSS. |
||||
* @payload: IEs that are announced by discovered BSS in its MGMt frames. |
||||
*/ |
||||
struct qlink_event_scan_result { |
||||
struct qlink_event ehdr; |
||||
__le64 tsf; |
||||
__le16 freq; |
||||
__le16 capab; |
||||
__le16 bintval; |
||||
s8 signal; |
||||
u8 frame_type; |
||||
u8 bssid[ETH_ALEN]; |
||||
u8 ssid_len; |
||||
u8 ssid[IEEE80211_MAX_SSID_LEN]; |
||||
u8 payload[0]; |
||||
} __packed; |
||||
|
||||
/**
|
||||
* enum qlink_scan_complete_flags - indicates result of scan request. |
||||
* |
||||
* @QLINK_SCAN_NONE: Scan request was processed. |
||||
* @QLINK_SCAN_ABORTED: Scan was aborted. |
||||
*/ |
||||
enum qlink_scan_complete_flags { |
||||
QLINK_SCAN_NONE = 0, |
||||
QLINK_SCAN_ABORTED = BIT(0), |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_event_scan_complete - data for QLINK_EVENT_SCAN_COMPLETE event |
||||
* |
||||
* @flags: flags indicating the status of pending scan request, |
||||
* see &enum qlink_scan_complete_flags. |
||||
*/ |
||||
struct qlink_event_scan_complete { |
||||
struct qlink_event ehdr; |
||||
__le32 flags; |
||||
} __packed; |
||||
|
||||
/* QLINK TLVs (Type-Length Values) definitions
|
||||
*/ |
||||
|
||||
enum qlink_tlv_id { |
||||
QTN_TLV_ID_FRAG_THRESH = 0x0201, |
||||
QTN_TLV_ID_RTS_THRESH = 0x0202, |
||||
QTN_TLV_ID_SRETRY_LIMIT = 0x0203, |
||||
QTN_TLV_ID_LRETRY_LIMIT = 0x0204, |
||||
QTN_TLV_ID_BCN_PERIOD = 0x0205, |
||||
QTN_TLV_ID_DTIM = 0x0206, |
||||
QTN_TLV_ID_CHANNEL = 0x020F, |
||||
QTN_TLV_ID_COVERAGE_CLASS = 0x0213, |
||||
QTN_TLV_ID_IFACE_LIMIT = 0x0214, |
||||
QTN_TLV_ID_NUM_IFACE_COMB = 0x0215, |
||||
QTN_TLV_ID_STA_BASIC_COUNTERS = 0x0300, |
||||
QTN_TLV_ID_STA_GENERIC_INFO = 0x0301, |
||||
QTN_TLV_ID_KEY = 0x0302, |
||||
QTN_TLV_ID_SEQ = 0x0303, |
||||
QTN_TLV_ID_CRYPTO = 0x0304, |
||||
QTN_TLV_ID_IE_SET = 0x0305, |
||||
}; |
||||
|
||||
struct qlink_tlv_hdr { |
||||
__le16 type; |
||||
__le16 len; |
||||
u8 val[0]; |
||||
} __packed; |
||||
|
||||
struct qlink_iface_limit { |
||||
__le16 max_num; |
||||
__le16 type_mask; |
||||
} __packed; |
||||
|
||||
struct qlink_iface_comb_num { |
||||
__le16 iface_comb_num; |
||||
} __packed; |
||||
|
||||
struct qlink_sta_stat_basic_counters { |
||||
__le64 rx_bytes; |
||||
__le64 tx_bytes; |
||||
__le64 rx_beacons; |
||||
__le32 rx_packets; |
||||
__le32 tx_packets; |
||||
__le32 rx_dropped; |
||||
__le32 tx_failed; |
||||
} __packed; |
||||
|
||||
enum qlink_sta_info_rate_flags { |
||||
QLINK_STA_INFO_RATE_FLAG_INVALID = 0, |
||||
QLINK_STA_INFO_RATE_FLAG_HT_MCS = BIT(0), |
||||
QLINK_STA_INFO_RATE_FLAG_VHT_MCS = BIT(1), |
||||
QLINK_STA_INFO_RATE_FLAG_SHORT_GI = BIT(2), |
||||
QLINK_STA_INFO_RATE_FLAG_60G = BIT(3), |
||||
}; |
||||
|
||||
enum qlink_sta_info_rate_bw { |
||||
QLINK_STA_INFO_RATE_BW_5 = 0, |
||||
QLINK_STA_INFO_RATE_BW_10 = 1, |
||||
QLINK_STA_INFO_RATE_BW_20 = 2, |
||||
QLINK_STA_INFO_RATE_BW_40 = 3, |
||||
QLINK_STA_INFO_RATE_BW_80 = 4, |
||||
QLINK_STA_INFO_RATE_BW_160 = 5, |
||||
}; |
||||
|
||||
/**
|
||||
* struct qlink_sta_info_rate - STA rate statistics |
||||
* |
||||
* @rate: data rate in Mbps. |
||||
* @flags: bitmap of &enum qlink_sta_flags. |
||||
* @mcs: 802.11-defined MCS index. |
||||
* nss: Number of Spatial Streams. |
||||
* @bw: bandwidth, one of &enum qlink_sta_info_rate_bw. |
||||
*/ |
||||
struct qlink_sta_info_rate { |
||||
__le16 rate; |
||||
u8 flags; |
||||
u8 mcs; |
||||
u8 nss; |
||||
u8 bw; |
||||
} __packed; |
||||
|
||||
struct qlink_sta_info_state { |
||||
__le32 mask; |
||||
__le32 value; |
||||
} __packed; |
||||
|
||||
#define QLINK_RSSI_OFFSET 120 |
||||
|
||||
struct qlink_sta_info_generic { |
||||
struct qlink_sta_info_state state; |
||||
__le32 connected_time; |
||||
__le32 inactive_time; |
||||
struct qlink_sta_info_rate rx_rate; |
||||
struct qlink_sta_info_rate tx_rate; |
||||
u8 rssi; |
||||
u8 rssi_avg; |
||||
} __packed; |
||||
|
||||
struct qlink_tlv_frag_rts_thr { |
||||
struct qlink_tlv_hdr hdr; |
||||
__le16 thr; |
||||
} __packed; |
||||
|
||||
struct qlink_tlv_rlimit { |
||||
struct qlink_tlv_hdr hdr; |
||||
u8 rlimit; |
||||
} __packed; |
||||
|
||||
struct qlink_tlv_cclass { |
||||
struct qlink_tlv_hdr hdr; |
||||
u8 cclass; |
||||
} __packed; |
||||
|
||||
enum qlink_dfs_state { |
||||
QLINK_DFS_USABLE, |
||||
QLINK_DFS_UNAVAILABLE, |
||||
QLINK_DFS_AVAILABLE, |
||||
}; |
||||
|
||||
enum qlink_channel_flags { |
||||
QLINK_CHAN_DISABLED = BIT(0), |
||||
QLINK_CHAN_NO_IR = BIT(1), |
||||
QLINK_CHAN_RADAR = BIT(3), |
||||
QLINK_CHAN_NO_HT40PLUS = BIT(4), |
||||
QLINK_CHAN_NO_HT40MINUS = BIT(5), |
||||
QLINK_CHAN_NO_OFDM = BIT(6), |
||||
QLINK_CHAN_NO_80MHZ = BIT(7), |
||||
QLINK_CHAN_NO_160MHZ = BIT(8), |
||||
QLINK_CHAN_INDOOR_ONLY = BIT(9), |
||||
QLINK_CHAN_IR_CONCURRENT = BIT(10), |
||||
QLINK_CHAN_NO_20MHZ = BIT(11), |
||||
QLINK_CHAN_NO_10MHZ = BIT(12), |
||||
}; |
||||
|
||||
struct qlink_tlv_channel { |
||||
struct qlink_tlv_hdr hdr; |
||||
__le16 hw_value; |
||||
__le16 center_freq; |
||||
__le32 flags; |
||||
u8 band; |
||||
u8 max_antenna_gain; |
||||
u8 max_power; |
||||
u8 max_reg_power; |
||||
__le32 dfs_cac_ms; |
||||
u8 dfs_state; |
||||
u8 beacon_found; |
||||
u8 rsvd[2]; |
||||
} __packed; |
||||
|
||||
#define QLINK_MAX_NR_CIPHER_SUITES 5 |
||||
#define QLINK_MAX_NR_AKM_SUITES 2 |
||||
|
||||
struct qlink_auth_encr { |
||||
__le32 wpa_versions; |
||||
__le32 cipher_group; |
||||
__le32 n_ciphers_pairwise; |
||||
__le32 ciphers_pairwise[QLINK_MAX_NR_CIPHER_SUITES]; |
||||
__le32 n_akm_suites; |
||||
__le32 akm_suites[QLINK_MAX_NR_AKM_SUITES]; |
||||
__le16 control_port_ethertype; |
||||
u8 auth_type; |
||||
u8 privacy; |
||||
u8 mfp; |
||||
u8 control_port; |
||||
u8 control_port_no_encrypt; |
||||
} __packed; |
||||
|
||||
#endif /* _QTN_QLINK_H_ */ |
@ -0,0 +1,71 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 Quantenna Communications, Inc. |
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <linux/nl80211.h> |
||||
|
||||
#include "qlink_util.h" |
||||
|
||||
u16 qlink_iface_type_mask_to_nl(u16 qlink_mask) |
||||
{ |
||||
u16 result = 0; |
||||
|
||||
if (qlink_mask & QLINK_IFTYPE_AP) |
||||
result |= BIT(NL80211_IFTYPE_AP); |
||||
|
||||
if (qlink_mask & QLINK_IFTYPE_STATION) |
||||
result |= BIT(NL80211_IFTYPE_STATION); |
||||
|
||||
if (qlink_mask & QLINK_IFTYPE_ADHOC) |
||||
result |= BIT(NL80211_IFTYPE_ADHOC); |
||||
|
||||
if (qlink_mask & QLINK_IFTYPE_MONITOR) |
||||
result |= BIT(NL80211_IFTYPE_MONITOR); |
||||
|
||||
if (qlink_mask & QLINK_IFTYPE_WDS) |
||||
result |= BIT(NL80211_IFTYPE_WDS); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
u8 qlink_chan_width_mask_to_nl(u16 qlink_mask) |
||||
{ |
||||
u8 result = 0; |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_5) |
||||
result |= BIT(NL80211_CHAN_WIDTH_5); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_10) |
||||
result |= BIT(NL80211_CHAN_WIDTH_10); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_20_NOHT) |
||||
result |= BIT(NL80211_CHAN_WIDTH_20_NOHT); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_20) |
||||
result |= BIT(NL80211_CHAN_WIDTH_20); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_40) |
||||
result |= BIT(NL80211_CHAN_WIDTH_40); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_80) |
||||
result |= BIT(NL80211_CHAN_WIDTH_80); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_80P80) |
||||
result |= BIT(NL80211_CHAN_WIDTH_80P80); |
||||
|
||||
if (qlink_mask & QLINK_CHAN_WIDTH_160) |
||||
result |= BIT(NL80211_CHAN_WIDTH_160); |
||||
|
||||
return result; |
||||
} |
@ -0,0 +1,80 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_QLINK_UTIL_H_ |
||||
#define _QTN_FMAC_QLINK_UTIL_H_ |
||||
|
||||
#include <linux/types.h> |
||||
#include <linux/skbuff.h> |
||||
|
||||
#include "qlink.h" |
||||
|
||||
static inline void qtnf_cmd_skb_put_action(struct sk_buff *skb, u16 action) |
||||
{ |
||||
__le16 *buf_ptr; |
||||
|
||||
buf_ptr = (__le16 *)skb_put(skb, sizeof(action)); |
||||
*buf_ptr = cpu_to_le16(action); |
||||
} |
||||
|
||||
static inline void |
||||
qtnf_cmd_skb_put_buffer(struct sk_buff *skb, const u8 *buf_src, size_t len) |
||||
{ |
||||
u8 *buf_dst; |
||||
|
||||
buf_dst = skb_put(skb, len); |
||||
memcpy(buf_dst, buf_src, len); |
||||
} |
||||
|
||||
static inline void qtnf_cmd_skb_put_tlv_arr(struct sk_buff *skb, |
||||
u16 tlv_id, const u8 arr[], |
||||
size_t arr_len) |
||||
{ |
||||
struct qlink_tlv_hdr *hdr = |
||||
(void *)skb_put(skb, sizeof(*hdr) + arr_len); |
||||
|
||||
hdr->type = cpu_to_le16(tlv_id); |
||||
hdr->len = cpu_to_le16(arr_len); |
||||
memcpy(hdr->val, arr, arr_len); |
||||
} |
||||
|
||||
static inline void qtnf_cmd_skb_put_tlv_u8(struct sk_buff *skb, u16 tlv_id, |
||||
u8 value) |
||||
{ |
||||
struct qlink_tlv_hdr *hdr = |
||||
(void *)skb_put(skb, sizeof(*hdr) + sizeof(value)); |
||||
|
||||
hdr->type = cpu_to_le16(tlv_id); |
||||
hdr->len = cpu_to_le16(sizeof(value)); |
||||
*hdr->val = value; |
||||
} |
||||
|
||||
static inline void qtnf_cmd_skb_put_tlv_u16(struct sk_buff *skb, |
||||
u16 tlv_id, u16 value) |
||||
{ |
||||
struct qlink_tlv_hdr *hdr = |
||||
(void *)skb_put(skb, sizeof(*hdr) + sizeof(value)); |
||||
__le16 tmp = cpu_to_le16(value); |
||||
|
||||
hdr->type = cpu_to_le16(tlv_id); |
||||
hdr->len = cpu_to_le16(sizeof(value)); |
||||
memcpy(hdr->val, &tmp, sizeof(tmp)); |
||||
} |
||||
|
||||
u16 qlink_iface_type_mask_to_nl(u16 qlink_mask); |
||||
u8 qlink_chan_width_mask_to_nl(u16 qlink_mask); |
||||
|
||||
#endif /* _QTN_FMAC_QLINK_UTIL_H_ */ |
@ -0,0 +1,32 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_HW_IDS_H_ |
||||
#define _QTN_HW_IDS_H_ |
||||
|
||||
#include <linux/pci_ids.h> |
||||
|
||||
#define PCIE_VENDOR_ID_QUANTENNA (0x1bb5) |
||||
|
||||
/* PCIE Device IDs */ |
||||
|
||||
#define PCIE_DEVICE_ID_QTN_PEARL (0x0008) |
||||
|
||||
/* FW names */ |
||||
|
||||
#define QTN_PCI_PEARL_FW_NAME "qtn/fmac_qsr10g.img" |
||||
|
||||
#endif /* _QTN_HW_IDS_H_ */ |
@ -0,0 +1,176 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <linux/types.h> |
||||
#include <linux/io.h> |
||||
|
||||
#include "shm_ipc.h" |
||||
|
||||
#undef pr_fmt |
||||
#define pr_fmt(fmt) "qtnfmac shm_ipc: %s: " fmt, __func__ |
||||
|
||||
static bool qtnf_shm_ipc_has_new_data(struct qtnf_shm_ipc *ipc) |
||||
{ |
||||
const u32 flags = readl(&ipc->shm_region->headroom.hdr.flags); |
||||
|
||||
return (flags & QTNF_SHM_IPC_NEW_DATA); |
||||
} |
||||
|
||||
static void qtnf_shm_handle_new_data(struct qtnf_shm_ipc *ipc) |
||||
{ |
||||
size_t size; |
||||
bool rx_buff_ok = true; |
||||
struct qtnf_shm_ipc_region_header __iomem *shm_reg_hdr; |
||||
|
||||
shm_reg_hdr = &ipc->shm_region->headroom.hdr; |
||||
|
||||
size = readw(&shm_reg_hdr->data_len); |
||||
|
||||
if (unlikely(size == 0 || size > QTN_IPC_MAX_DATA_SZ)) { |
||||
pr_err("wrong rx packet size: %zu\n", size); |
||||
rx_buff_ok = false; |
||||
} else { |
||||
memcpy_fromio(ipc->rx_data, ipc->shm_region->data, size); |
||||
} |
||||
|
||||
writel(QTNF_SHM_IPC_ACK, &shm_reg_hdr->flags); |
||||
readl(&shm_reg_hdr->flags); /* flush PCIe write */ |
||||
|
||||
ipc->interrupt.fn(ipc->interrupt.arg); |
||||
|
||||
if (likely(rx_buff_ok)) { |
||||
ipc->rx_packet_count++; |
||||
ipc->rx_callback.fn(ipc->rx_callback.arg, ipc->rx_data, size); |
||||
} |
||||
} |
||||
|
||||
static void qtnf_shm_ipc_irq_work(struct work_struct *work) |
||||
{ |
||||
struct qtnf_shm_ipc *ipc = container_of(work, struct qtnf_shm_ipc, |
||||
irq_work); |
||||
|
||||
while (qtnf_shm_ipc_has_new_data(ipc)) |
||||
qtnf_shm_handle_new_data(ipc); |
||||
} |
||||
|
||||
static void qtnf_shm_ipc_irq_inbound_handler(struct qtnf_shm_ipc *ipc) |
||||
{ |
||||
u32 flags; |
||||
|
||||
flags = readl(&ipc->shm_region->headroom.hdr.flags); |
||||
|
||||
if (flags & QTNF_SHM_IPC_NEW_DATA) |
||||
queue_work(ipc->workqueue, &ipc->irq_work); |
||||
} |
||||
|
||||
static void qtnf_shm_ipc_irq_outbound_handler(struct qtnf_shm_ipc *ipc) |
||||
{ |
||||
u32 flags; |
||||
|
||||
if (!READ_ONCE(ipc->waiting_for_ack)) |
||||
return; |
||||
|
||||
flags = readl(&ipc->shm_region->headroom.hdr.flags); |
||||
|
||||
if (flags & QTNF_SHM_IPC_ACK) { |
||||
WRITE_ONCE(ipc->waiting_for_ack, 0); |
||||
complete(&ipc->tx_completion); |
||||
} |
||||
} |
||||
|
||||
int qtnf_shm_ipc_init(struct qtnf_shm_ipc *ipc, |
||||
enum qtnf_shm_ipc_direction direction, |
||||
struct qtnf_shm_ipc_region __iomem *shm_region, |
||||
struct workqueue_struct *workqueue, |
||||
const struct qtnf_shm_ipc_int *interrupt, |
||||
const struct qtnf_shm_ipc_rx_callback *rx_callback) |
||||
{ |
||||
BUILD_BUG_ON(offsetof(struct qtnf_shm_ipc_region, data) != |
||||
QTN_IPC_REG_HDR_SZ); |
||||
BUILD_BUG_ON(sizeof(struct qtnf_shm_ipc_region) > QTN_IPC_REG_SZ); |
||||
|
||||
ipc->shm_region = shm_region; |
||||
ipc->direction = direction; |
||||
ipc->interrupt = *interrupt; |
||||
ipc->rx_callback = *rx_callback; |
||||
ipc->tx_packet_count = 0; |
||||
ipc->rx_packet_count = 0; |
||||
ipc->workqueue = workqueue; |
||||
ipc->waiting_for_ack = 0; |
||||
ipc->tx_timeout_count = 0; |
||||
|
||||
switch (direction) { |
||||
case QTNF_SHM_IPC_OUTBOUND: |
||||
ipc->irq_handler = qtnf_shm_ipc_irq_outbound_handler; |
||||
break; |
||||
case QTNF_SHM_IPC_INBOUND: |
||||
ipc->irq_handler = qtnf_shm_ipc_irq_inbound_handler; |
||||
break; |
||||
default: |
||||
return -EINVAL; |
||||
} |
||||
|
||||
INIT_WORK(&ipc->irq_work, qtnf_shm_ipc_irq_work); |
||||
init_completion(&ipc->tx_completion); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void qtnf_shm_ipc_free(struct qtnf_shm_ipc *ipc) |
||||
{ |
||||
complete_all(&ipc->tx_completion); |
||||
} |
||||
|
||||
int qtnf_shm_ipc_send(struct qtnf_shm_ipc *ipc, const u8 *buf, size_t size) |
||||
{ |
||||
int ret = 0; |
||||
struct qtnf_shm_ipc_region_header __iomem *shm_reg_hdr; |
||||
|
||||
shm_reg_hdr = &ipc->shm_region->headroom.hdr; |
||||
|
||||
if (unlikely(size > QTN_IPC_MAX_DATA_SZ)) |
||||
return -E2BIG; |
||||
|
||||
ipc->tx_packet_count++; |
||||
|
||||
writew(size, &shm_reg_hdr->data_len); |
||||
memcpy_toio(ipc->shm_region->data, buf, size); |
||||
|
||||
/* sync previous writes before proceeding */ |
||||
dma_wmb(); |
||||
|
||||
WRITE_ONCE(ipc->waiting_for_ack, 1); |
||||
|
||||
/* sync previous memory write before announcing new data ready */ |
||||
wmb(); |
||||
|
||||
writel(QTNF_SHM_IPC_NEW_DATA, &shm_reg_hdr->flags); |
||||
readl(&shm_reg_hdr->flags); /* flush PCIe write */ |
||||
|
||||
ipc->interrupt.fn(ipc->interrupt.arg); |
||||
|
||||
if (!wait_for_completion_timeout(&ipc->tx_completion, |
||||
QTN_SHM_IPC_ACK_TIMEOUT)) { |
||||
ret = -ETIMEDOUT; |
||||
ipc->tx_timeout_count++; |
||||
pr_err("TX ACK timeout\n"); |
||||
} |
||||
|
||||
/* now we're not waiting for ACK even in case of timeout */ |
||||
WRITE_ONCE(ipc->waiting_for_ack, 0); |
||||
|
||||
return ret; |
||||
} |
@ -0,0 +1,80 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_SHM_IPC_H_ |
||||
#define _QTN_FMAC_SHM_IPC_H_ |
||||
|
||||
#include <linux/workqueue.h> |
||||
#include <linux/completion.h> |
||||
#include <linux/mutex.h> |
||||
#include <linux/spinlock.h> |
||||
|
||||
#include "shm_ipc_defs.h" |
||||
|
||||
#define QTN_SHM_IPC_ACK_TIMEOUT (2 * HZ) |
||||
|
||||
struct qtnf_shm_ipc_int { |
||||
void (*fn)(void *arg); |
||||
void *arg; |
||||
}; |
||||
|
||||
struct qtnf_shm_ipc_rx_callback { |
||||
void (*fn)(void *arg, const u8 *buf, size_t len); |
||||
void *arg; |
||||
}; |
||||
|
||||
enum qtnf_shm_ipc_direction { |
||||
QTNF_SHM_IPC_OUTBOUND = BIT(0), |
||||
QTNF_SHM_IPC_INBOUND = BIT(1), |
||||
}; |
||||
|
||||
struct qtnf_shm_ipc { |
||||
struct qtnf_shm_ipc_region __iomem *shm_region; |
||||
enum qtnf_shm_ipc_direction direction; |
||||
size_t tx_packet_count; |
||||
size_t rx_packet_count; |
||||
|
||||
size_t tx_timeout_count; |
||||
|
||||
u8 waiting_for_ack; |
||||
|
||||
u8 rx_data[QTN_IPC_MAX_DATA_SZ] __aligned(sizeof(u32)); |
||||
|
||||
struct qtnf_shm_ipc_int interrupt; |
||||
struct qtnf_shm_ipc_rx_callback rx_callback; |
||||
|
||||
void (*irq_handler)(struct qtnf_shm_ipc *ipc); |
||||
|
||||
struct workqueue_struct *workqueue; |
||||
struct work_struct irq_work; |
||||
struct completion tx_completion; |
||||
}; |
||||
|
||||
int qtnf_shm_ipc_init(struct qtnf_shm_ipc *ipc, |
||||
enum qtnf_shm_ipc_direction direction, |
||||
struct qtnf_shm_ipc_region __iomem *shm_region, |
||||
struct workqueue_struct *workqueue, |
||||
const struct qtnf_shm_ipc_int *interrupt, |
||||
const struct qtnf_shm_ipc_rx_callback *rx_callback); |
||||
void qtnf_shm_ipc_free(struct qtnf_shm_ipc *ipc); |
||||
int qtnf_shm_ipc_send(struct qtnf_shm_ipc *ipc, const u8 *buf, size_t size); |
||||
|
||||
static inline void qtnf_shm_ipc_irq_handler(struct qtnf_shm_ipc *ipc) |
||||
{ |
||||
ipc->irq_handler(ipc); |
||||
} |
||||
|
||||
#endif /* _QTN_FMAC_SHM_IPC_H_ */ |
@ -0,0 +1,46 @@ |
||||
/*
|
||||
* Copyright (c) 2015-2016 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef _QTN_FMAC_SHM_IPC_DEFS_H_ |
||||
#define _QTN_FMAC_SHM_IPC_DEFS_H_ |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
#define QTN_IPC_REG_HDR_SZ (32) |
||||
#define QTN_IPC_REG_SZ (4096) |
||||
#define QTN_IPC_MAX_DATA_SZ (QTN_IPC_REG_SZ - QTN_IPC_REG_HDR_SZ) |
||||
|
||||
enum qtnf_shm_ipc_region_flags { |
||||
QTNF_SHM_IPC_NEW_DATA = BIT(0), |
||||
QTNF_SHM_IPC_ACK = BIT(1), |
||||
}; |
||||
|
||||
struct qtnf_shm_ipc_region_header { |
||||
__le32 flags; |
||||
__le16 data_len; |
||||
} __packed; |
||||
|
||||
union qtnf_shm_ipc_region_headroom { |
||||
struct qtnf_shm_ipc_region_header hdr; |
||||
u8 headroom[QTN_IPC_REG_HDR_SZ]; |
||||
} __packed; |
||||
|
||||
struct qtnf_shm_ipc_region { |
||||
union qtnf_shm_ipc_region_headroom headroom; |
||||
u8 data[QTN_IPC_MAX_DATA_SZ]; |
||||
} __packed; |
||||
|
||||
#endif /* _QTN_FMAC_SHM_IPC_DEFS_H_ */ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue