From: Zhirun Liu Add the LMAC message definitions, IPC host support, firmware command/event handling and platform bring-up required by the AIC8800D80 U02 FullMAC firmware. The interface is scoped to the current in-tree kernel APIs and the SDIO station baseline selected by the first patch. Signed-off-by: Zhirun Liu Signed-off-by: Yanli Yang --- .../aic/aic8800_fdrv/aic_bsp_export.h | 62 + .../net/wireless/aic/aic8800_fdrv/aic_bus.h | 191 + .../wireless/aic/aic8800_fdrv/aicwf_debug.h | 37 + .../net/wireless/aic/aic8800_fdrv/hal_desc.h | 359 ++ .../wireless/aic/aic8800_fdrv/ipc_compat.h | 25 + .../net/wireless/aic/aic8800_fdrv/ipc_host.c | 42 + .../net/wireless/aic/aic8800_fdrv/ipc_host.h | 169 + .../wireless/aic/aic8800_fdrv/ipc_shared.h | 770 ++++ .../net/wireless/aic/aic8800_fdrv/lmac_mac.h | 437 ++ .../net/wireless/aic/aic8800_fdrv/lmac_msg.h | 3101 ++++++++++++++ .../wireless/aic/aic8800_fdrv/lmac_types.h | 22 + .../wireless/aic/aic8800_fdrv/reg_access.h | 146 + .../net/wireless/aic/aic8800_fdrv/rwnx_cmds.c | 471 +++ .../net/wireless/aic/aic8800_fdrv/rwnx_cmds.h | 99 + .../wireless/aic/aic8800_fdrv/rwnx_compat.h | 80 + .../net/wireless/aic/aic8800_fdrv/rwnx_defs.h | 704 ++++ .../wireless/aic/aic8800_fdrv/rwnx_events.h | 938 +++++ .../net/wireless/aic/aic8800_fdrv/rwnx_irqs.c | 49 + .../net/wireless/aic/aic8800_fdrv/rwnx_irqs.h | 12 + .../wireless/aic/aic8800_fdrv/rwnx_msg_rx.c | 1614 ++++++++ .../wireless/aic/aic8800_fdrv/rwnx_msg_rx.h | 8 + .../wireless/aic/aic8800_fdrv/rwnx_msg_tx.c | 3619 +++++++++++++++++ .../wireless/aic/aic8800_fdrv/rwnx_msg_tx.h | 266 ++ .../wireless/aic/aic8800_fdrv/rwnx_platform.c | 2868 +++++++++++++ .../wireless/aic/aic8800_fdrv/rwnx_platform.h | 183 + .../net/wireless/aic/aic8800_fdrv/rwnx_prof.h | 142 + .../net/wireless/aic/aic8800_fdrv/rwnx_strs.c | 269 ++ .../net/wireless/aic/aic8800_fdrv/rwnx_strs.h | 31 + .../wireless/aic/aic8800_fdrv/rwnx_utils.c | 35 + .../wireless/aic/aic8800_fdrv/rwnx_utils.h | 131 + .../net/wireless/aic/aic8800_fdrv/rwnx_v7.c | 181 + .../net/wireless/aic/aic8800_fdrv/rwnx_v7.h | 21 + 32 files changed, 17082 insertions(+) create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/reg_access.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c create mode 100644 drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h diff --git a/drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h b/drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h new file mode 100644 index 0000000000000..11b414df42ce3 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/aic_bsp_export.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __AIC_BSP_EXPORT_H +#define __AIC_BSP_EXPORT_H + +#include + +enum aicbsp_subsys { + AIC_BLUETOOTH, + AIC_WIFI, +}; + +enum aicbsp_pwr_state { + AIC_PWR_OFF, + AIC_PWR_ON, +}; + +struct aicbsp_feature_t { + int hwinfo; + u32 sdio_clock; + u8 sdio_phase; + int fwlog_en; + u8 irqf; + u8 cpmode; + int adapt; +}; + +enum skb_buff_id { + AIC_RESV_MEM_TXDATA, +}; + +#ifdef CONFIG_DPD +struct rf_misc_ram_t { + u32 bit_mask[3]; + u32 reserved; + u32 dpd_high[96]; + u32 dpd_11b[96]; + u32 dpd_low[96]; + u32 idac_11b[48]; + u32 idac_high[48]; + u32 idac_low[48]; + u32 loft_res[18]; + u32 rx_iqim_res[16]; +}; + +struct rf_misc_ram_lite_t { + u32 bit_mask[4]; + u32 dpd_high[96]; + u32 loft_res[18]; +}; + +#define MEMBER_SIZE(type, member) sizeof_field(type, member) +#define DPD_RESULT_SIZE_8800DC sizeof(struct rf_misc_ram_lite_t) + +extern struct rf_misc_ram_lite_t dpd_res; +#endif + +int aicbsp_set_subsys(int, int); +int aicbsp_get_feature(struct aicbsp_feature_t *feature); +struct sk_buff *aicbsp_resv_mem_alloc_skb(unsigned int length, uint32_t id); +void aicbsp_resv_mem_kfree_skb(struct sk_buff *skb, uint32_t id); + +#endif diff --git a/drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h b/drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h new file mode 100644 index 0000000000000..2498d3d592b42 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/aic_bus.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 AIC semiconductor. + * + * @file aic_bus.h + * @brief Common bus abstraction layer for AIC8800 series. + * + * This header defines the bus-level operations that abstract the + * underlying transport (SDIO, USB, etc.) from the upper driver layers. + * The design follows the pattern used by silabs/wfx and broadcom/b43. + * + * Key principles: + * - struct aicwf_bus_ops provides a complete set of low-level bus + * primitives (read/write registers, send/recv packets, flow control, + * power management, interrupt handling). + * - Upper-layer code accesses the bus only through the ops table and + * inline wrappers defined here. It MUST NOT dereference the + * bus-private structure (aic_sdio_dev etc.) directly. + * - When a new transport (e.g. USB) is added, only the bus_ops + * implementation needs to change; upper layers are unaffected. + */ + +#ifndef _AIC_BUS_H_ +#define _AIC_BUS_H_ + +#include +#include +#include + +/* Bus operational state */ +enum aicwf_bus_state { BUS_DOWN_ST, BUS_UP_ST }; + +struct aicwf_bus { + union { + struct aic_sdio_dev *sdio; + struct aic_usb_dev *usb; + } bus_priv; + struct device *dev; + struct aicwf_bus_ops *ops; + enum aicwf_bus_state state; + u8 *cmd_buf; + struct completion bustx_trgg; + struct completion busrx_trgg; + struct completion busirq_trgg; // new oob feature + struct task_struct *bustx_thread; + struct task_struct *busrx_thread; + struct task_struct *busirq_thread; // new oob feature +}; + +/* ================================================================ + * Bus-level operation vectors + * + * Every transport (SDIO, USB, ...) provides one instance of this + * struct. The bus-private data pointer is carried inside + * struct aicwf_bus so that ops callbacks can retrieve it via + * bus->bus_priv. + * ================================================================ + */ + +struct aicwf_bus_ops { + /* ----- Lifecycle ----- */ + int (*start)(struct device *dev); + void (*stop)(struct device *dev); + + /* ----- Data / message transmission ----- */ + int (*txdata)(struct device *dev, struct sk_buff *skb); + int (*txmsg)(struct device *dev, u8 *msg, uint len); + + /* ----- Low-level register access ----- + * These replace the direct aicwf_sdio_readb/writeb calls. + */ + int (*read_reg)(struct device *dev, u32 regaddr, u8 *val); + int (*write_reg)(struct device *dev, u32 regaddr, u8 val); + + /* ----- Bulk data transfer ----- + * send_pkt – write a buffer to the chip's TX FIFO + * recv_pkt – read a buffer from the chip's RX FIFO + */ + int (*send_pkt)(struct device *dev, u8 *buf, uint count); + int (*recv_pkt)(struct device *dev, u8 *buf, u32 size); + + /* ----- Interrupt handling ----- */ + int (*enable_irq)(struct device *dev); + void (*disable_irq)(struct device *dev); + + /* ----- Flow control ----- + * Returns true if the device is ready to accept more data. + */ + bool (*flow_ctrl)(struct device *dev); + + /* ----- Power management ----- */ + int (*sleep_allow)(struct device *dev); + int (*wakeup)(struct device *dev); + + /* ----- Chip-specific hardware info ----- + * Returns a pointer to chip hardware properties (SDIO register + * layout, feature flags, etc.). The caller treats this as + * opaque; only the bus layer interprets it. + */ + const void *(*get_hw_props)(struct device *dev); +}; + +/* ================================================================ + * Inline wrappers – upper layers call these instead of touching + * bus_priv internals. + * ================================================================ + */ + +static inline int aicwf_bus_start(struct aicwf_bus *bus) +{ + return bus->ops->start(bus->dev); +} + +static inline void aicwf_bus_stop(struct aicwf_bus *bus) +{ + bus->ops->stop(bus->dev); +} + +static inline int aicwf_bus_txdata(struct aicwf_bus *bus, struct sk_buff *skb) +{ + return bus->ops->txdata(bus->dev, skb); +} + +static inline int aicwf_bus_txmsg(struct aicwf_bus *bus, u8 *msg, uint len) +{ + return bus->ops->txmsg(bus->dev, msg, len); +} + +static inline int aicwf_bus_read_reg(struct aicwf_bus *bus, + u32 regaddr, u8 *val) +{ + return bus->ops->read_reg(bus->dev, regaddr, val); +} + +static inline int aicwf_bus_write_reg(struct aicwf_bus *bus, + u32 regaddr, u8 val) +{ + return bus->ops->write_reg(bus->dev, regaddr, val); +} + +static inline int aicwf_bus_send_pkt(struct aicwf_bus *bus, + u8 *buf, uint count) +{ + return bus->ops->send_pkt(bus->dev, buf, count); +} + +static inline int aicwf_bus_recv_pkt(struct aicwf_bus *bus, + u8 *buf, u32 size) +{ + return bus->ops->recv_pkt(bus->dev, buf, size); +} + +static inline int aicwf_bus_enable_irq(struct aicwf_bus *bus) +{ + return bus->ops->enable_irq(bus->dev); +} + +static inline void aicwf_bus_disable_irq(struct aicwf_bus *bus) +{ + bus->ops->disable_irq(bus->dev); +} + +static inline bool aicwf_bus_flow_ctrl(struct aicwf_bus *bus) +{ + return bus->ops->flow_ctrl(bus->dev); +} + +static inline int aicwf_bus_sleep_allow(struct aicwf_bus *bus) +{ + return bus->ops->sleep_allow(bus->dev); +} + +static inline int aicwf_bus_wakeup(struct aicwf_bus *bus) +{ + return bus->ops->wakeup(bus->dev); +} + +static inline const void *aicwf_bus_get_hw_props(struct aicwf_bus *bus) +{ + return bus->ops->get_hw_props(bus->dev); +} + +/* ================================================================ + * Bus-level init / deinit (shared across transports) + * ================================================================ + */ + +int aicwf_bus_init(uint bus_hdrlen, struct device *dev); +void aicwf_bus_deinit(struct device *dev); + +#endif /* _AIC_BUS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h b/drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h new file mode 100644 index 0000000000000..4a49c16f15a31 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/aicwf_debug.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _AICWF_DEBUG_H_ +#define _AICWF_DEBUG_H_ + +#define RWNX_FN_ENTRY_STR ">>> %s()\n", __func__ + +/* message levels */ +#define LOGERROR 0x0001 +#define LOGINFO 0x0002 +#define LOGTRACE 0x0004 +#define LOGDEBUG 0x0008 +#define LOGDATA 0x0010 +#define LOGIRQ 0x0020 +#define LOGSDPWRC 0x0040 +#define LOGWAKELOCK 0x0080 +#define LOGRXPOLL 0x0100 + +extern int aicwf_dbg_level; +void rwnx_data_dump(char *tag, void *data, unsigned long len); + +#define AICWF_LOG "AICWFDBG(" + +#define AICWFDBG(level, args, arg...) \ + do { \ + if (aicwf_dbg_level & (level)) { \ + pr_debug(AICWF_LOG #level ")\t" args, ##arg); \ + } \ + } while (0) + +#define RWNX_DBG(fmt, ...) \ + do { \ + if (aicwf_dbg_level & LOGTRACE) { \ + pr_debug(AICWF_LOG "LOGTRACE)\t" fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#endif /* _AICWF_DEBUG_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h b/drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h new file mode 100644 index 0000000000000..35809ccd5cef5 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/hal_desc.h @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file hal_desc.c + * + * @brief File containing the definition of HW descriptors + * + ****************************************************************************** + */ + +#ifndef _HAL_DESC_H_ +#define _HAL_DESC_H_ + +#include "lmac_types.h" +#include "rwnx_compat.h" + +/* Rate and policy table */ + +#define N_CCK 8 +#define N_OFDM 8 +#define N_HT (8 * 2 * 2 * 4) +#define N_VHT (10 * 4 * 2 * 8) +#define N_HE_SU (12 * 4 * 3 * 8) +#define N_HE_MU (12 * 6 * 3 * 8) +#define N_HE_ER (3 * 3 + 3) // RU242 + RU106 + +/* conversion table from NL80211 to MACHW enum */ +extern const int chnl2bw[]; + +/* conversion table from MACHW to NL80211 enum */ +extern const int bw2chnl[]; + +/* Values for formatModTx */ +#define FORMATMOD_NON_HT 0 +#define FORMATMOD_NON_HT_DUP_OFDM 1 +#define FORMATMOD_HT_MF 2 +#define FORMATMOD_HT_GF 3 +#define FORMATMOD_VHT 4 +#define FORMATMOD_HE_SU 5 +#define FORMATMOD_HE_MU 6 +#define FORMATMOD_HE_ER 7 +#define FORMATMOD_HE_TB 8 + +/* Values for navProtFrmEx */ +#define NAV_PROT_NO_PROT_BIT 0 +#define NAV_PROT_SELF_CTS_BIT 1 +#define NAV_PROT_RTS_CTS_BIT 2 +#define NAV_PROT_RTS_CTS_WITH_QAP_BIT 3 +#define NAV_PROT_STBC_BIT 4 + +/* THD MACCTRLINFO2 fields, used in struct umacdesc umac.flags */ +/// WhichDescriptor definition - contains aMPDU bit and position value +/// Offset of WhichDescriptor field in the MAC CONTROL INFO 2 word +#define WHICHDESC_OFT 19 +/// Mask of the WhichDescriptor field +#define WHICHDESC_MSK (0x07 << WHICHDESC_OFT) +/// Only 1 THD possible, describing an unfragmented MSDU +#define WHICHDESC_UNFRAGMENTED_MSDU (0x00 << WHICHDESC_OFT) +/// THD describing the first MPDU of a fragmented MSDU +#define WHICHDESC_FRAGMENTED_MSDU_FIRST (0x01 << WHICHDESC_OFT) +/// THD describing intermediate MPDUs of a fragmented MSDU +#define WHICHDESC_FRAGMENTED_MSDU_INT (0x02 << WHICHDESC_OFT) +/// THD describing the last MPDU of a fragmented MSDU +#define WHICHDESC_FRAGMENTED_MSDU_LAST (0x03 << WHICHDESC_OFT) +/// THD for extra descriptor starting an AMPDU +#define WHICHDESC_AMPDU_EXTRA (0x04 << WHICHDESC_OFT) +/// THD describing the first MPDU of an A-MPDU +#define WHICHDESC_AMPDU_FIRST (0x05 << WHICHDESC_OFT) +/// THD describing intermediate MPDUs of an A-MPDU +#define WHICHDESC_AMPDU_INT (0x06 << WHICHDESC_OFT) +/// THD describing the last MPDU of an A-MPDU +#define WHICHDESC_AMPDU_LAST (0x07 << WHICHDESC_OFT) + +/// aMPDU bit offset +#define AMPDU_OFT 21 +/// aMPDU bit +#define AMPDU_BIT CO_BIT(AMPDU_OFT) + +union rwnx_mcs_index { + struct { + u32 mcs : 3; + u32 nss : 2; + } ht; + struct { + u32 mcs : 4; + u32 nss : 3; + } vht; + struct { + u32 mcs : 4; + u32 nss : 3; + } he; + u32 legacy : 7; +}; + +/* c.f RW-WLAN-nX-MAC-HW-UM */ +union rwnx_rate_ctrl_info { + struct { + u32 mcs_index_tx : 7; + u32 bw_tx : 2; + u32 gi_and_pre_type_tx : 2; + u32 format_mod_tx : 3; + u32 nav_prot_frm_ex : 3; + u32 mcs_index_prot_tx : 7; + u32 bw_prot_tx : 2; + u32 format_mod_prot_tx : 3; + u32 n_retry : 3; + }; + u32 value; +}; + +/* c.f RW-WLAN-nX-MAC-HW-UM */ +struct rwnx_power_ctrl_info { + u32 tx_pwr_level_pt : 8; + u32 tx_pwr_level_prot_pt : 8; + u32 reserved : 16; +}; + +/* c.f RW-WLAN-nX-MAC-HW-UM */ +union rwnx_pol_phy_ctrl_info_1 { + struct { + u32 rsvd1 : 3; + u32 bf_frm_ex : 1; + u32 num_extn_ss : 2; + u32 fec_coding : 1; + u32 stbc : 2; + u32 rsvd2 : 5; + u32 n_tx : 3; + u32 n_tx_prot : 3; + }; + u32 value; +}; + +/* c.f RW-WLAN-nX-MAC-HW-UM */ +union rwnx_pol_phy_ctrl_info_2 { + struct { + u32 antenna_set : 8; + u32 smm_index : 8; + u32 beam_formed : 1; + }; + u32 value; +}; + +/* c.f RW-WLAN-nX-MAC-HW-UM */ +union rwnx_pol_mac_ctrl_info_1 { + struct { + u32 key_sram_index : 10; + u32 key_sram_index_ra : 10; + }; + u32 value; +}; + +/* c.f RW-WLAN-nX-MAC-HW-UM */ +union rwnx_pol_mac_ctrl_info_2 { + struct { + u32 long_retry_limit : 8; + u32 short_retry_limit : 8; + u32 rts_threshold : 12; + }; + u32 value; +}; + +#define POLICY_TABLE_PATTERN 0xBADCAB1E + +struct tx_policy_tbl { + /* Unique Pattern at the start of Policy Table */ + u32 upatterntx; + /* PHY Control 1 Information used by MAC HW */ + union rwnx_pol_phy_ctrl_info_1 phyctrlinfo_1; + /* PHY Control 2 Information used by MAC HW */ + union rwnx_pol_phy_ctrl_info_2 phyctrlinfo_2; + /* MAC Control 1 Information used by MAC HW */ + union rwnx_pol_mac_ctrl_info_1 macctrlinfo_1; + /* MAC Control 2 Information used by MAC HW */ + union rwnx_pol_mac_ctrl_info_2 macctrlinfo_2; + + union rwnx_rate_ctrl_info ratectrlinfos[NX_TX_MAX_RATES]; + struct rwnx_power_ctrl_info powerctrlinfos[NX_TX_MAX_RATES]; +}; + +#ifdef CONFIG_RWNX_FULLMAC + +/** + * union rwnx_hw_txstatus - Bitfield of confirmation status + * + * @tx_done: Packet has been processed by the firmware + * @retry_required: Packet was transmitted but not acknowledged and must be + * submitted again by the driver + * @sw_retry_required: Packet was not transmitted by the firmware and must be + * submitted again by the driver + * @acknowledged: Packet was acknowledged by the peer + * @reserved: Reserved status bits + * @value: Complete status word + */ +union rwnx_hw_txstatus { + struct { + u32 tx_done : 1; + u32 retry_required : 1; + u32 sw_retry_required : 1; + u32 acknowledged : 1; + u32 reserved : 28; + }; + u32 value; +}; + +/** + * struct tx_cfm_tag - Structure indicating the status and other + * information about the transmission + * + * @credits: Number of credits returned to the TX queue (0 or 1) + * @ampdu_size: Number of frames in the A-MPDU when this was its last frame, + * 0 when this was not the last frame, or 1 for a singleton + * @amsdu_size: Maximum A-MSDU size in bytes + * @status: Transmission status + * @hostid: Host identifier of the transmitted buffer + */ +struct tx_cfm_tag { + /* + * u16 pn[4]; + * u16 sn; + * u16 timestamp; + */ + s8 credits; + u8 ampdu_size; +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + u16 amsdu_size; +#endif + union rwnx_hw_txstatus status; + u32 hostid; +}; + +/** + * struct rwnx_hw_txhdr - Hardware part of tx header + * + * @cfm: Information updated by fw/hardware after sending a frame + */ +struct rwnx_hw_txhdr { + struct tx_cfm_tag cfm; +}; + +#endif /* CONFIG_RWNX_FULLMAC */ + +/* Modem */ + +#define MDM_PHY_CONFIG_TRIDENT 0 +#define MDM_PHY_CONFIG_ELMA 1 +#define MDM_PHY_CONFIG_KARST 2 + +// MODEM features (from reg_mdm_stat.h) +/// MUMIMOTX field bit +#define MDM_MUMIMOTX_BIT ((u32)0x80000000) +/// MUMIMOTX field position +#define MDM_MUMIMOTX_POS 31 +/// MUMIMORX field bit +#define MDM_MUMIMORX_BIT ((u32)0x40000000) +/// MUMIMORX field position +#define MDM_MUMIMORX_POS 30 +/// BFMER field bit +#define MDM_BFMER_BIT ((u32)0x20000000) +/// BFMER field position +#define MDM_BFMER_POS 29 +/// BFMEE field bit +#define MDM_BFMEE_BIT ((u32)0x10000000) +/// BFMEE field position +#define MDM_BFMEE_POS 28 +/// LDPCDEC field bit +#define MDM_LDPCDEC_BIT ((u32)0x08000000) +/// LDPCDEC field position +#define MDM_LDPCDEC_POS 27 +/// LDPCENC field bit +#define MDM_LDPCENC_BIT ((u32)0x04000000) +/// LDPCENC field position +#define MDM_LDPCENC_POS 26 +/// CHBW field mask +#define MDM_CHBW_MASK ((u32)0x03000000) +/// CHBW field LSB position +#define MDM_CHBW_LSB 24 +/// CHBW field width +#define MDM_CHBW_WIDTH ((u32)0x00000002) +/// DSSSCCK field bit +#define MDM_DSSSCCK_BIT ((u32)0x00800000) +/// DSSSCCK field position +#define MDM_DSSSCCK_POS 23 +/// VHT field bit +#define MDM_VHT_BIT ((u32)0x00400000) +/// VHT field position +#define MDM_VHT_POS 22 +/// HE field bit +#define MDM_HE_BIT ((u32)0x00200000) +/// HE field position +#define MDM_HE_POS 21 +/// ESS field bit +#define MDM_ESS_BIT ((u32)0x00100000) +/// ESS field position +#define MDM_ESS_POS 20 +/// RFMODE field mask +#define MDM_RFMODE_MASK ((u32)0x000F0000) +/// RFMODE field LSB position +#define MDM_RFMODE_LSB 16 +/// RFMODE field width +#define MDM_RFMODE_WIDTH ((u32)0x00000004) +/// NSTS field mask +#define MDM_NSTS_MASK ((u32)0x0000F000) +/// NSTS field LSB position +#define MDM_NSTS_LSB 12 +/// NSTS field width +#define MDM_NSTS_WIDTH ((u32)0x00000004) +/// NSS field mask +#define MDM_NSS_MASK ((u32)0x00000F00) +/// NSS field LSB position +#define MDM_NSS_LSB 8 +/// NSS field width +#define MDM_NSS_WIDTH ((u32)0x00000004) +/// NTX field mask +#define MDM_NTX_MASK ((u32)0x000000F0) +/// NTX field LSB position +#define MDM_NTX_LSB 4 +/// NTX field width +#define MDM_NTX_WIDTH ((u32)0x00000004) +/// NRX field mask +#define MDM_NRX_MASK ((u32)0x0000000F) +/// NRX field LSB position +#define MDM_NRX_LSB 0 +/// NRX field width +#define MDM_NRX_WIDTH ((u32)0x00000004) + +#define __MDM_PHYCFG_FROM_VERS(v) (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB) + +#define RIU_FCU_PRESENT_MASK ((u32)0xFF000000) +#define RIU_FCU_PRESENT_LSB 24 + +#define __RIU_FCU_PRESENT(v) \ + (((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5) + +/// AGC load version field mask +#define RIU_AGC_LOAD_MASK ((u32)0x00C00000) +/// AGC load version field LSB position +#define RIU_AGC_LOAD_LSB 22 + +#define __RIU_AGCLOAD_FROM_VERS(v) \ + (((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB) + +#define __FPGA_TYPE(v) (((v) & 0xFFFF0000) >> 16) + +#define __MDM_MAJOR_VERSION(v) (((v) & 0xFF000000) >> 24) +#define __MDM_MINOR_VERSION(v) (((v) & 0x00FF0000) >> 16) +/* + * #define __MDM_VERSION(v) \ + * ((__MDM_MAJOR_VERSION(v) + 2) * 10 + __MDM_MINOR_VERSION(v)) + */ +#define __MDM_VERSION(v) \ +({ \ + u32 __v = (v); \ + (__MDM_MAJOR_VERSION(__v) + 2) * 10 + __MDM_MINOR_VERSION(__v); \ +}) + +#endif // _HAL_DESC_H_ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h b/drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h new file mode 100644 index 0000000000000..cf1fe0d737b84 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_compat.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + **************************************************************************************** + */ + +#ifndef _IPC_H_ +#define _IPC_H_ + +#define __INLINE inline + +#define __ALIGN4 __aligned(4) + +#define ASSERT_ERR(condition) \ + do { \ + if (unlikely(!(condition))) { \ + pr_err("%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__, \ + __LINE__); \ + } \ + } while (0) + +#endif /* _IPC_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c new file mode 100644 index 0000000000000..5962d8b3fcf3f --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief IPC module + * + ****************************************************************************** + */ + +#ifndef __KERNEL__ +#include +#else +#include "rwnx_defs.h" +#include "rwnx_prof.h" +#include +#endif + +#include "ipc_host.h" + +/* + * TYPES DEFINITION + ****************************************************************************** + */ + +const int nx_txdesc_cnt[] = { + NX_TXDESC_CNT0, NX_TXDESC_CNT1, NX_TXDESC_CNT2, NX_TXDESC_CNT3, +#if NX_TXQ_CNT == 5 + NX_TXDESC_CNT4, +#endif +}; + +const int nx_txuser_cnt[] = { + CONFIG_USER_MAX, + CONFIG_USER_MAX, + CONFIG_USER_MAX, + CONFIG_USER_MAX, +#if NX_TXQ_CNT == 5 + 1, +#endif +}; diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h new file mode 100644 index 0000000000000..b1cafbf30f73d --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_host.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief IPC module + * + ****************************************************************************** + */ + +#ifndef _IPC_HOST_H_ +#define _IPC_HOST_H_ + +/* + * INCLUDE FILES + ****************************************************************************** + */ +#include "ipc_shared.h" +#ifndef __KERNEL__ +#include "arch.h" +#else +#include "ipc_compat.h" +#endif + +/* + ****************************************************************************** + * @brief This structure is used to initialize the MAC SW + * + * The WLAN device driver provides functions call-back with this structure + ****************************************************************************** + */ +struct ipc_host_cb_tag { + /// WLAN driver call-back function: send_data_cfm + int (*send_data_cfm)(void *pthis, void *host_id); + + /// WLAN driver call-back function: recv_data_ind + uint8_t (*recv_data_ind)(void *pthis, void *host_id); + + /// WLAN driver call-back function: recv_radar_ind + uint8_t (*recv_radar_ind)(void *pthis, void *host_id); + + /// WLAN driver call-back function: recv_unsup_rx_vec_ind + uint8_t (*recv_unsup_rx_vec_ind)(void *pthis, void *host_id); + + /// WLAN driver call-back function: recv_msg_ind + uint8_t (*recv_msg_ind)(void *pthis, void *host_id); + + /// WLAN driver call-back function: recv_msgack_ind + uint8_t (*recv_msgack_ind)(void *pthis, void *host_id); + + /// WLAN driver call-back function: recv_dbg_ind + uint8_t (*recv_dbg_ind)(void *pthis, void *host_id); + + /// WLAN driver call-back function: prim_tbtt_ind + void (*prim_tbtt_ind)(void *pthis); + + /// WLAN driver call-back function: sec_tbtt_ind + void (*sec_tbtt_ind)(void *pthis); +}; + +/* + * Struct used to store information about host buffers (DMA Address and local + * pointer) + */ +struct ipc_hostbuf { + void *hostid; ///< ptr to hostbuf client (ipc_host client) structure + u32 dma_addr; ///< ptr to real hostbuf dma address +}; + +/// Definition of the IPC Host environment structure. +struct ipc_host_env_tag { + /// Structure containing the callback pointers + struct ipc_host_cb_tag cb; + + /// Pointer to the shared environment + struct ipc_shared_env_tag *shared; + +#ifdef CONFIG_RWNX_FULLMAC + // Array used to store the descriptor addresses + struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT]; + // Index of the host RX descriptor array (ipc_shared environment) + u8 ipc_host_rxdesc_idx; + /// Store the number of RX Descriptors + u8 rxdesc_nb; +#endif //(CONFIG_RWNX_FULLMAC) + + /// Fields for Data Rx handling + // Index used for ipc_host_rxbuf_array to point to current buffer + u8 ipc_host_rxbuf_idx; + // Store the number of Rx Data buffers + u32 rx_bufnb; + // Store the size of the Rx Data buffers + u32 rx_bufsz; + + /// Fields for Radar events handling + // Global array used to store the hostid and hostbuf addresses + struct ipc_hostbuf ipc_host_radarbuf_array[IPC_RADARBUF_CNT]; + // Index used for ipc_host_rxbuf_array to point to current buffer + u8 ipc_host_radarbuf_idx; + // Store the number of radar event buffers + u32 radar_bufnb; + // Store the size of the radar event buffers + u32 radar_bufsz; + + /// Fields for Unsupported frame handling + // Global array used to store the hostid and hostbuf addresses + struct ipc_hostbuf ipc_host_unsuprxvecbuf_array[IPC_UNSUPRXVECBUF_CNT]; + // Index used for ipc_host_unsuprxvecbuf_array to point to current buffer + u8 ipc_host_unsuprxvecbuf_idx; + // Store the number of unsupported rx vector buffers + u32 unsuprxvec_bufnb; + // Store the size of unsupported rx vector buffers + u32 unsuprxvec_bufsz; + + // Index used that points to the first free TX desc + u32 txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX]; + // Index used that points to the first used TX desc + u32 txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX]; + // Array storing the currently pushed host ids for the BK queue + void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0]; + // Array storing the currently pushed host ids for the BE queue + void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1]; + // Array storing the currently pushed host ids for the VI queue + void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2]; + // Array storing the currently pushed host ids for the VO queue + void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3]; +#if NX_TXQ_CNT == 5 + // Array storing the currently pushed host ids for the BCN queue + void *tx_host_id4[1][NX_TXDESC_CNT4]; +#endif + // Pointer to the different host ids arrays, per IPC queue + void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX]; + // Pointer to the different TX descriptor arrays, per IPC queue + struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX]; + + /// Fields for Emb->App MSGs handling + // Global array used to store the hostid and hostbuf addresses for msg/ind + struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT]; + // Index of the MSG E2A buffers array to point to current buffer + u8 ipc_host_msge2a_idx; + // Store the number of E2A MSG buffers + u32 ipc_e2amsg_bufnb; + // Store the size of the E2A MSG buffers + u32 ipc_e2amsg_bufsz; + + /// E2A ACKs of A2E MSGs + u8 msga2e_cnt; + void *msga2e_hostid; + + /// Fields for Debug MSGs handling + // Global array used to store the hostid and hostbuf addresses for Debug + // messages + struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT]; + // Index of the Debug messages buffers array to point to current buffer + u8 ipc_host_dbg_idx; + // Store the number of Debug messages buffers + u32 ipc_dbg_bufnb; + // Store the size of the Debug messages buffers + u32 ipc_dbg_bufsz; + + /// Pointer to the attached object (used in callbacks and register accesses) + void *pthis; +}; + +extern const int nx_txdesc_cnt[]; +extern const int nx_txuser_cnt[]; + +#endif // _IPC_HOST_H_ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h b/drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h new file mode 100644 index 0000000000000..5773567c65adc --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/ipc_shared.h @@ -0,0 +1,770 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Shared data between both IPC modules + * + ****************************************************************************** + */ + +#ifndef _IPC_SHARED_H_ +#define _IPC_SHARED_H_ + +/* + * INCLUDE FILES + **************************************************************************************** + */ +#include "ipc_compat.h" +#include "lmac_mac.h" +#include "rwnx_compat.h" + +/* + * DEFINES AND MACROS + **************************************************************************************** + */ +#define CO_BIT(pos) (1U << (pos)) + +#define IPC_TXQUEUE_CNT NX_TXQ_CNT +#define NX_TXDESC_CNT0 8 +#define NX_TXDESC_CNT1 64 +#define NX_TXDESC_CNT2 64 +#define NX_TXDESC_CNT3 32 +#if NX_TXQ_CNT == 5 +#define NX_TXDESC_CNT4 8 +#endif + +/* + * Number of Host buffers available for Data Rx handling (through DMA) + */ +#define IPC_RXBUF_CNT 128 + +/* + * Number of shared descriptors available for Data RX handling + */ +#define IPC_RXDESC_CNT 128 + +/* + * Number of Host buffers available for Radar events handling (through DMA) + */ +#define IPC_RADARBUF_CNT 16 + +/* + * Number of Host buffers available for unsupported Rx vectors handling (through + * DMA) + */ +#define IPC_UNSUPRXVECBUF_CNT 8 + +/* + * Size of RxVector + */ +#define IPC_RXVEC_SIZE 16 + +/* + * Number of Host buffers available for Emb->App MSGs sending (through DMA) + */ +#ifdef CONFIG_RWNX_FULLMAC +#define IPC_MSGE2A_BUF_CNT 64 +#endif +/* + * Number of Host buffers available for Debug Messages sending (through DMA) + */ +#define IPC_DBGBUF_CNT 32 + +/* + * Length used in MSGs structures + */ +#define IPC_A2E_MSG_BUF_SIZE 127 // size in 4-byte words +#ifdef CONFIG_RWNX_FULLMAC +#define IPC_E2A_MSG_SIZE_BASE 256 // size in 4-byte words +#endif + +#define IPC_E2A_MSG_PARAM_SIZE IPC_E2A_MSG_SIZE_BASE + +/* + * Debug messages buffers size (in bytes) + */ +#define IPC_DBG_PARAM_SIZE 256 + +/* + * Define used for Rx hostbuf validity. + * This value should appear only when hostbuf was used for a Reception. + */ +#define RX_DMA_OVER_PATTERN 0xAAAAAA00 + +/* + * Define used for MSG buffers validity. + * This value will be written only when a MSG buffer is used for sending from + * Emb to App. + */ +#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A + +/* + * Define used for Debug messages buffers validity. + * This value will be written only when a DBG buffer is used for sending from + * Emb to App. + */ +#define IPC_DBG_VALID_PATTERN 0x000CACA0 + +/* + * Length of the receive vectors, in bytes + */ +#define DMA_HDR_PHYVECT_LEN 36 + +/* + * Maximum number of payload addresses and lengths present in the descriptor + */ +#ifdef CONFIG_RWNX_SPLIT_TX_BUF +#define NX_TX_PAYLOAD_MAX 6 +#else +#define NX_TX_PAYLOAD_MAX 1 +#endif + +/* + * Message struct/ID API version + */ +#define MSG_API_VER 33 + +/* + **************************************************************************************** + */ +// c.f LMAC/src/tx/tx_swdesc.h +/// Descriptor filled by the Host +struct hostdesc { + /// Pointer to packet payload + // u32 packet_addr; + /// Size of the payload + u16 packet_len; + u16 flags_ext; + + u32 hostid; +#ifdef CONFIG_RWNX_FULLMAC + /// Address of the status descriptor in host memory (used for confirmation + /// upload) + // u32 status_desc_addr; + /// Destination Address + struct mac_addr eth_dest_addr; + /// Source Address + struct mac_addr eth_src_addr; + /// Ethernet Type + __be16 ethertype; +#else /* ! CONFIG_RWNX_FULLMAC */ +#ifdef CONFIG_RWNX_AGG_TX + /// Sequence Number for AMPDU MPDUs - for quick check if it's allowed within + /// window + u16 sn; +#endif /* CONFIG_RWNX_AGG_TX */ + /// Padding between the buffer control structure and the MPDU in host memory + u8 padding; +#endif /* CONFIG_RWNX_FULLMAC */ + u8 ac; + /// Packet TID (0xFF if not a QoS frame) + u8 tid; + /// Interface Id + u8 vif_idx; + /// Station Id (0xFF if station is unknown) + u8 staid; +#ifdef CONFIG_RWNX_MUMIMO_TX + /// MU-MIMO information (GroupId and User Position in the group) - The + /// GroupId is located on bits 0-5 and the User Position on bits 6-7. The + /// GroupId value is set to 63 if MU-MIMO shall not be used + u8 mumimo_info; +#endif /* CONFIG_RWNX_MUMIMO_TX */ +#ifdef CONFIG_RWNX_FULLMAC + /// TX flags + u16 flags; +#endif /* CONFIG_RWNX_FULLMAC */ +}; + +/// Descriptor filled by the UMAC +struct umacdesc { +#ifdef CONFIG_RWNX_AGG_TX + /// First Sequence Number of the BlockAck window + u16 sn_win; + /// Flags from UMAC (match tx_hd.macctrlinfo2 format) + u32 flags; + /// PHY related flags field - rate, GI type, BW type - filled by driver + u32 phy_flags; +#endif //(CONFIG_RWNX_AGG_TX) +}; + +struct txdesc_api { + /// Information provided by Host + struct hostdesc host; +}; + +struct txdesc_host { + u32 ready; + + /// API of the embedded part + struct txdesc_api api; +}; + +/// Comes from ipc_dma.h +/// Element in the pool of TX DMA bridge descriptors. +struct dma_desc { + /* Application subsystem address which is used as source address for DMA + * payload transfer + */ + u32 src; + /** Points to the start of the embedded data buffer associated with this + * descriptor. This address acts as the destination address for the DMA + * payload transfer + */ + u32 dest; + /// Complete length of the buffer in memory + u16 length; + /// Control word for the DMA engine (e.g. for interrupt generation) + u16 ctrl; + /// Pointer to the next element of the chained list + u32 next; +}; + +// Comes from la.h +/// Length of the configuration data of a logic analyzer +#define LA_CONF_LEN 10 + +/// Structure containing the configuration data of a logic analyzer +struct la_conf_tag { + u32 conf[LA_CONF_LEN]; + u32 trace_len; + u32 diag_conf; +}; + +/// Size of a logic analyzer memory +#define LA_MEM_LEN (1024 * 1024) + +/// Type of errors +enum { + /// Recoverable error, not requiring any action from Upper MAC + DBG_ERROR_RECOVERABLE = 0, + /// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart + /// operation + DBG_ERROR_FATAL +}; + +/// Maximum length of the SW diag trace +#define DBG_SW_DIAG_MAX_LEN 1024 + +/// Maximum length of the error trace +#define DBG_ERROR_TRACE_SIZE 256 + +/// Number of MAC diagnostic port banks +#define DBG_DIAGS_MAC_MAX 48 + +/// Number of PHY diagnostic port banks +#define DBG_DIAGS_PHY_MAX 32 + +/// Maximum size of the RX header descriptor information in the debug dump +#define DBG_RHD_MEM_LEN (5 * 1024) + +/// Maximum size of the RX buffer descriptor information in the debug dump +#define DBG_RBD_MEM_LEN (5 * 1024) + +/// Maximum size of the TX header descriptor information in the debug dump +#define DBG_THD_MEM_LEN (10 * 1024) + +/// Structure containing the information about the PHY channel that is used +struct phy_channel_info { + /// PHY channel information 1 + u32 info1; + /// PHY channel information 2 + u32 info2; +}; + +/// Debug information forwarded to host when an error occurs +struct dbg_debug_info_tag { + /// Type of error (0: recoverable, 1: fatal) + u32 error_type; + /// Pointer to the first RX Header Descriptor chained to the MAC HW + u32 rhd; + /// Size of the RX header descriptor buffer + u32 rhd_len; + /// Pointer to the first RX Buffer Descriptor chained to the MAC HW + u32 rbd; + /// Size of the RX buffer descriptor buffer + u32 rbd_len; + /// Pointer to the first TX Header Descriptors chained to the MAC HW + u32 thd[NX_TXQ_CNT]; + /// Size of the TX header descriptor buffer + u32 thd_len[NX_TXQ_CNT]; + /// MAC HW diag configuration + u32 hw_diag; + /// Error message + u32 error[DBG_ERROR_TRACE_SIZE / 4]; + /// SW diag configuration length + u32 sw_diag_len; + /// SW diag configuration + u32 sw_diag[DBG_SW_DIAG_MAX_LEN / 4]; + /// PHY channel information + struct phy_channel_info chan_info; + /// Embedded LA configuration + struct la_conf_tag la_conf; + /// MAC diagnostic port state + u16 diags_mac[DBG_DIAGS_MAC_MAX]; + /// PHY diagnostic port state + u16 diags_phy[DBG_DIAGS_PHY_MAX]; + /// MAC HW RX Header descriptor pointer + u32 rhd_hw_ptr; + /// MAC HW RX Buffer descriptor pointer + u32 rbd_hw_ptr; +}; + +/// Full debug dump that is forwarded to host in case of error +struct dbg_debug_dump_tag { + /// Debug information + struct dbg_debug_info_tag dbg_info; + + /// RX header descriptor memory + u32 rhd_mem[DBG_RHD_MEM_LEN / 4]; + + /// RX buffer descriptor memory + u32 rbd_mem[DBG_RBD_MEM_LEN / 4]; + + /// TX header descriptor memory + u32 thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN / 4]; + + /// Logic analyzer memory + u32 la_mem[LA_MEM_LEN / 4]; +}; + +/// Number of pulses in a radar event structure +#define RADAR_PULSE_MAX 4 + +/// Definition of an array of radar pulses +struct radar_pulse_array_desc { + /// Buffer containing the radar pulses + u32 pulse[RADAR_PULSE_MAX]; + /// Index of the radar detection chain that detected those pulses + u32 idx; + /// Number of valid pulses in the buffer + u32 cnt; +}; + +/// Bit mapping inside a radar pulse element +struct radar_pulse { + s32 + freq : 6; /** Freq (resolution is 2Mhz range is [-Fadc/4 .. Fadc/4]) */ + u32 fom : 4; /** Figure of Merit */ + u32 len : 6; /** Length of the current radar pulse (resolution is 2us) */ + u32 rep : 16; /* Time interval between the previous radar event + * and the current one (in us) + */ +}; + +/// Definition of a RX vector descriptor +struct rx_vector_desc { + /// PHY channel information + struct phy_channel_info phy_info; + + /// RX vector 1 + u32 rx_vect1[IPC_RXVEC_SIZE / 4]; + + /// Used to print a valid rx vector + u32 pattern; +}; + +/// +struct rxdesc_tag { + /// Host Buffer Address + u32 host_id; + /// Length + u32 frame_len; + /// Status + u16 status; +}; + +/* + **************************************************************************************** + * @addtogroup IPC_TX IPC Tx path + * @ingroup IPC + * @brief IPC Tx path structures and functions + * + * A typical use case of the IPC Tx path API: + * @msc + * hscale = "2"; + * + * a [label=Driver], + * b [label="IPC host"], + * c [label="IPC emb"], + * d [label=Firmware]; + * + * --- [label="Tx descriptor queue example"]; + * a=>a [label="Driver receives a Tx packet from OS"]; + * a=>b [label="ipc_host_txdesc_get()"]; + * a<a [label="Driver fill the descriptor"]; + * a=>b [label="ipc_host_txdesc_push()"]; + * ... [label="(several Tx desc can be pushed)"]; + * b:>c [label="Tx desc queue filled IRQ"]; + * c=>>d [label="EDCA sub-scheduler callback"]; + * c<>d [label="UMAC Tx desc callback"]; + * ... [label="(several Tx desc can be popped)"]; + * d=>d [label="Packets are sent or discarded"]; + * --- [label="Tx confirm queue example"]; + * c<=d [label="ipc_emb_txcfm_push()"]; + * c>>d [label="Request accepted"]; + * ... [label="(several Tx cfm can be pushed)"]; + * b<:c [label="Tx cfm queue filled IRQ"]; + * a<<=b [label="Driver's Tx Confirm callback"]; + * a=>b [label="ipc_host_txcfm_pop()"]; + * a<c [label="ipc_host_rxbuf_push()"]; + * d=>c [label="ipc_host_rxbuf_push()"]; + * d=>c [label="ipc_host_rxbuf_push()"]; + * ... [label="(several Rx buffer are pushed)"]; + * a=>a [label=" Frame is received\n from the medium"]; + * a<a [label=" Firmware fill the buffer\n with received frame"]; + * a<c [label="Rx desc queue filled IRQ"]; + * c=>>d [label="Driver Rx packet callback"]; + * c<=d [label="ipc_host_rxdesc_pop()"]; + * d=>d [label="Rx packet is handed\nover to the OS "]; + * ... [label="(several Rx desc can be poped)"]; + * --- [label="Rx buffer request exemple"]; + * b:>c [label="Low Rx buffer count IRQ"]; + * a<>d [label="Driver Rx buffer callback"]; + * d=>c [label="ipc_host_rxbuf_push()"]; + * d=>c [label="ipc_host_rxbuf_push()"]; + * d=>c [label="ipc_host_rxbuf_push()"]; + * ... [label="(several Rx buffer are pushed)"]; + * @endmsc + * + * @addtogroup IPC_RX + * @{ + **************************************************************************************** + */ + +/// @} IPC_RX + +/* + **************************************************************************************** + * @defgroup IPC_MISC IPC Misc + * @ingroup IPC + * @brief IPC miscellaneous functions + **************************************************************************************** + */ +/** IPC header structure. This structure is stored at the beginning of every + * IPC message. + * @warning This structure's size must NOT exceed 4 bytes in length. + */ +struct ipc_header { + /// IPC message type. + u16 type; + /// IPC message size in number of bytes. + u16 size; +}; + +struct ipc_msg_elt { + /// Message header (alignment forced on word size, see allocation in shared + /// env). + struct ipc_header header __ALIGN4; +}; + +/// Message structure for MSGs from Emb to App +struct ipc_e2a_msg { + u16 id; ///< Message id. + u16 dummy_dest_id; + u16 dummy_src_id; + u16 param_len; ///< Parameter embedded struct length. + u32 pattern; ///< Used to stamp a valid MSG buffer + u32 param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be + ///< word-aligned. +}; + +/// Message structure for Debug messages from Emb to App +struct ipc_dbg_msg { + u32 string[IPC_DBG_PARAM_SIZE / 4]; ///< Debug string + u32 pattern; ///< Used to stamp a valid buffer +}; + +/// Message structure for MSGs from App to Emb. +/// Actually a sub-structure will be used when filling the messages. +struct ipc_a2e_msg { + u32 dummy_word; // used to cope with kernel message structure + u32 msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg +}; + +struct ipc_shared_rx_buf { + /// < ptr to hostbuf client (ipc_host client) structure + u32 hostid; + /// < ptr to real hostbuf dma address + u32 dma_addr; +}; + +struct ipc_shared_rx_desc { + /// DMA Address + u32 dma_addr; +}; + +/// Structure containing FW characteristics for compatibility checking +struct compatibility_tag { + /// Size of IPC shared memory + u16 ipc_shared_size; + /// Message struct/ID API version + u16 msg_api; + /// Version of IPC shared + u8 ipc_shared_version; + /// Number of host buffers available for Emb->App MSGs sending + u8 msge2a_buf_cnt; + /// Number of host buffers available for Debug Messages sending + u8 dbgbuf_cnt; + /// Number of host buffers available for Radar events handling + u8 radarbuf_cnt; + /// Number of host buffers available for unsupported Rx vectors handling + u8 unsuprxvecbuf_cnt; + /// Number of shared descriptors available for Data RX handling + u8 rxdesc_cnt; + /// Number of host buffers available for Data Rx handling + u8 rxbuf_cnt; + /// Number of descriptors in BK TX queue (power of 2, min 4, max 64) + u8 bk_txq; + /// Number of descriptors in BE TX queue (power of 2, min 4, max 64) + u8 be_txq; + /// Number of descriptors in VI TX queue (power of 2, min 4, max 64) + u8 vi_txq; + /// Number of descriptors in VO TX queue (power of 2, min 4, max 64) + u8 vo_txq; + /// Number of descriptors in BCN TX queue (power of 2, min 4, max 64) + u8 bcn_txq; +}; + +/* + * TYPE and STRUCT DEFINITIONS + **************************************************************************************** + */ + +// Indexes are defined in the MIB shared structure +struct ipc_shared_env_tag { + struct compatibility_tag comp_info; // FW characteristics + + struct ipc_a2e_msg + msg_a2e_buf; // room for MSG to be sent from App to Emb + + // Fields for MSGs sending from Emb to App + struct ipc_e2a_msg + msg_e2a_buf; // room to build the MSG to be DMA Xferred + struct dma_desc + msg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers + u32 msg_e2a_hostbuf_addr[IPC_MSGE2A_BUF_CNT]; // buffers @ for DMA Xfers + + // Fields for Debug MSGs sending from Emb to App + struct ipc_dbg_msg + dbg_buf; // room to build the MSG to be DMA Xferred + struct dma_desc + dbg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers + u32 dbg_hostbuf_addr[IPC_DBGBUF_CNT]; // buffers @ for MSGs DMA Xfers + u32 la_dbginfo_addr; // Host buffer address for the debug information + u32 pattern_addr; + + u32 radarbuf_hostbuf[IPC_RADARBUF_CNT]; // buffers @ for Radar Events + u32 unsuprxvecbuf_hostbuf[IPC_UNSUPRXVECBUF_CNT]; // buffers @ for + // unsupported Rx vectors + struct txdesc_host txdesc0[CONFIG_USER_MAX][NX_TXDESC_CNT0]; + struct txdesc_host txdesc1[CONFIG_USER_MAX][NX_TXDESC_CNT1]; + struct txdesc_host txdesc2[CONFIG_USER_MAX][NX_TXDESC_CNT2]; + struct txdesc_host txdesc3[CONFIG_USER_MAX][NX_TXDESC_CNT3]; +#if NX_TXQ_CNT == 5 + struct txdesc_host txdesc4[1][NX_TXDESC_CNT4]; +#endif +#ifdef CONFIG_RWNX_FULLMAC + // RX Descriptors Array + struct ipc_shared_rx_desc host_rxdesc[IPC_RXDESC_CNT]; + // RX Buffers Array + struct ipc_shared_rx_buf host_rxbuf[IPC_RXBUF_CNT]; +#else + // buffers @ for Data Rx + u32 host_rxbuf[IPC_RXBUF_CNT]; +#endif /* CONFIG_RWNX_FULLMAC */ + + u32 buffered[NX_REMOTE_STA_MAX][TID_MAX]; + + u16 trace_pattern; + u32 trace_start; + u32 trace_end; + u32 trace_size; + u32 trace_offset; + u32 trace_nb_compo; + u32 trace_offset_compo; +}; + +extern struct ipc_shared_env_tag ipc_shared_env; + +/* + * TYPE and STRUCT DEFINITIONS + **************************************************************************************** + */ + +// IRQs from app to emb +/// Interrupts bits used for the TX descriptors of the AC queues +#ifdef CONFIG_RWNX_MUMIMO_TX +/// Interrupts bits used +#if CONFIG_USER_MAX > 3 +#define IPC_IRQ_A2E_USER_MSK 0xF +#elif CONFIG_USER_MAX > 2 +#define IPC_IRQ_A2E_USER_MSK 0x7 +#else +#define IPC_IRQ_A2E_USER_MSK 0x3 +#endif + +/// Offset of the interrupts for AC0 +#define IPC_IRQ_A2E_AC0_OFT 8 +/// Mask of the interrupts for AC0 +#define IPC_IRQ_A2E_AC0_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC0_OFT) +/// Offset of the interrupts for AC1 +#define IPC_IRQ_A2E_AC1_OFT (IPC_IRQ_A2E_AC0_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for AC1 +#define IPC_IRQ_A2E_AC1_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC1_OFT) +/// Offset of the interrupts for AC2 +#define IPC_IRQ_A2E_AC2_OFT (IPC_IRQ_A2E_AC1_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for AC2 +#define IPC_IRQ_A2E_AC2_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC2_OFT) +/// Offset of the interrupts for AC3 +#define IPC_IRQ_A2E_AC3_OFT (IPC_IRQ_A2E_AC2_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for AC3 +#define IPC_IRQ_A2E_AC3_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC3_OFT) +/// Offset of the interrupts for BCN +#define IPC_IRQ_A2E_BCN_OFT (IPC_IRQ_A2E_AC3_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for BCN +#define IPC_IRQ_A2E_BCN_MSK CO_BIT(IPC_IRQ_A2E_BCN_OFT) + +#define IPC_IRQ_A2E_AC_TXDESC \ + (IPC_IRQ_A2E_AC0_MSK | IPC_IRQ_A2E_AC1_MSK | IPC_IRQ_A2E_AC2_MSK | \ + IPC_IRQ_A2E_AC3_MSK) + +/// Interrupts bits used for the TX descriptors of the BCN queue +#if NX_TXQ_CNT < 5 +#define IPC_IRQ_A2E_BCN_TXDESC 0 +#else +#define IPC_IRQ_A2E_BCN_TXDESC (0x01 << IPC_IRQ_A2E_BCN_OFT) +#endif + +/// IPC TX descriptor interrupt mask +#define IPC_IRQ_A2E_TXDESC (IPC_IRQ_A2E_AC_TXDESC | IPC_IRQ_A2E_BCN_TXDESC) +#else +/// IPC TX descriptor interrupt mask +#define IPC_IRQ_A2E_TXDESC 0xFF00 +#endif + +#define IPC_IRQ_A2E_TXDESC_FIRSTBIT (8) +#define IPC_IRQ_A2E_RXBUF_BACK CO_BIT(5) +#define IPC_IRQ_A2E_RXDESC_BACK CO_BIT(4) + +#define IPC_IRQ_A2E_MSG CO_BIT(1) +#define IPC_IRQ_A2E_DBG CO_BIT(0) + +#define IPC_IRQ_A2E_ALL (IPC_IRQ_A2E_TXDESC | IPC_IRQ_A2E_MSG | IPC_IRQ_A2E_DBG) + +// IRQs from emb to app +#define IPC_IRQ_E2A_TXCFM_POS 7 + +#ifdef CONFIG_RWNX_MUMIMO_TX +/// Interrupts bits used +#if CONFIG_USER_MAX > 3 +#define IPC_IRQ_E2A_USER_MSK 0xF +#elif CONFIG_USER_MAX > 2 +#define IPC_IRQ_E2A_USER_MSK 0x7 +#else +#define IPC_IRQ_E2A_USER_MSK 0x3 +#endif + +/// Offset of the interrupts for AC0 +#define IPC_IRQ_E2A_AC0_OFT IPC_IRQ_E2A_TXCFM_POS +/// Mask of the interrupts for AC0 +#define IPC_IRQ_E2A_AC0_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC0_OFT) +/// Offset of the interrupts for AC1 +#define IPC_IRQ_E2A_AC1_OFT (IPC_IRQ_E2A_AC0_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for AC1 +#define IPC_IRQ_E2A_AC1_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC1_OFT) +/// Offset of the interrupts for AC2 +#define IPC_IRQ_E2A_AC2_OFT (IPC_IRQ_E2A_AC1_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for AC2 +#define IPC_IRQ_E2A_AC2_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC2_OFT) +/// Offset of the interrupts for AC3 +#define IPC_IRQ_E2A_AC3_OFT (IPC_IRQ_E2A_AC2_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for AC3 +#define IPC_IRQ_E2A_AC3_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC3_OFT) +/// Offset of the interrupts for BCN +#define IPC_IRQ_E2A_BCN_OFT (IPC_IRQ_E2A_AC3_OFT + CONFIG_USER_MAX) +/// Mask of the interrupts for BCN +#define IPC_IRQ_E2A_BCN_MSK CO_BIT(IPC_IRQ_E2A_BCN_OFT) + +#define IPC_IRQ_E2A_AC_TXCFM \ + (IPC_IRQ_E2A_AC0_MSK | IPC_IRQ_E2A_AC1_MSK | IPC_IRQ_E2A_AC2_MSK | \ + IPC_IRQ_E2A_AC3_MSK) + +/// Interrupts bits used for the TX descriptors of the BCN queue +#if NX_TXQ_CNT < 5 +#define IPC_IRQ_E2A_BCN_TXCFM 0 +#else +#define IPC_IRQ_E2A_BCN_TXCFM (0x01 << IPC_IRQ_E2A_BCN_OFT) +#endif + +/// IPC TX descriptor interrupt mask +#define IPC_IRQ_E2A_TXCFM (IPC_IRQ_E2A_AC_TXCFM | IPC_IRQ_E2A_BCN_TXCFM) + +#else + +#define IPC_IRQ_E2A_TXCFM (((1 << NX_TXQ_CNT) - 1) << IPC_IRQ_E2A_TXCFM_POS) + +#endif /* CONFIG_RWNX_MUMIMO_TX */ + +#define IPC_IRQ_E2A_UNSUP_RX_VEC CO_BIT(7) +#define IPC_IRQ_E2A_RADAR CO_BIT(6) +#define IPC_IRQ_E2A_TBTT_SEC CO_BIT(5) +#define IPC_IRQ_E2A_TBTT_PRIM CO_BIT(4) +#define IPC_IRQ_E2A_RXDESC CO_BIT(3) +#define IPC_IRQ_E2A_MSG_ACK CO_BIT(2) +#define IPC_IRQ_E2A_MSG CO_BIT(1) +#define IPC_IRQ_E2A_DBG CO_BIT(0) + +#define IPC_IRQ_E2A_ALL \ + (IPC_IRQ_E2A_TXCFM | IPC_IRQ_E2A_RXDESC | IPC_IRQ_E2A_MSG_ACK | \ + IPC_IRQ_E2A_MSG | IPC_IRQ_E2A_DBG | IPC_IRQ_E2A_TBTT_PRIM | \ + IPC_IRQ_E2A_TBTT_SEC | IPC_IRQ_E2A_RADAR | IPC_IRQ_E2A_UNSUP_RX_VEC) + +// FLAGS for RX desc +#define IPC_RX_FORWARD CO_BIT(1) +#define IPC_RX_INTRABSS CO_BIT(0) + +// IPC message TYPE +enum { + IPC_MSG_NONE = 0, + IPC_MSG_WRAP, + IPC_MSG_KMSG, + + IPC_DBG_STRING, + +}; + +#endif // _IPC_SHARED_H_ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h b/drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h new file mode 100644 index 0000000000000..114ed14f4ad55 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/lmac_mac.h @@ -0,0 +1,437 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief MAC related definitions. + * + * Adapted from mac_types.h to used lmac_types.h instead of standard types + * eg: perl -pi -e '$_ =~ s/uint(\d{1,2})_t/u$1_l/g; \ + * $_ =~ s/int(\d{1,2})_t/s$1_l/g; \ + * $_ =~ s/CO_BIT/BIT/g;' + * + **************************************************************************************** + */ + +#ifndef LMAC_MAC_H_ +#define LMAC_MAC_H_ + +#include "lmac_types.h" + +/// Interface types +enum mac_vif_type { + /// ESS STA interface + VIF_STA, + /// IBSS STA interface + VIF_IBSS, + /// AP interface + VIF_AP, + /// Mesh Point interface + VIF_MESH_POINT, + /// Monitor interface + VIF_MONITOR, + /// Unknown type + VIF_UNKNOWN +}; + +/// MAC address length in bytes. +#define MAC_ADDR_LEN 6 + +/// MAC address structure. +struct mac_addr { + /// Array of 16-bit words that make up the MAC address. + u16 array[MAC_ADDR_LEN / 2]; +}; + +/// SSID maximum length. +#define MAC_SSID_LEN 32 + +/// SSID. +struct mac_ssid { + /// Actual length of the SSID. + u8 length; + /// Array containing the SSID name. + u8 array[MAC_SSID_LEN]; +}; + +/// BSS type +enum mac_bss_type { + INFRASTRUCTURE_MODE = 1, + INDEPENDENT_BSS_MODE, + ANY_BSS_MODE +}; + +/// Channel Band +enum mac_chan_band { + /// 2.4GHz Band + PHY_BAND_2G4, + /// 5GHz band + PHY_BAND_5G, + /// Number of bands + PHY_BAND_MAX, +}; + +/// Operating Channel Bandwidth +enum mac_chan_bandwidth { + /// 20MHz BW + PHY_CHNL_BW_20, + /// 40MHz BW + PHY_CHNL_BW_40, + /// 80MHz BW + PHY_CHNL_BW_80, + /// 160MHz BW + PHY_CHNL_BW_160, + /// 80+80MHz BW + PHY_CHNL_BW_80P80, + /// Reserved BW + PHY_CHNL_BW_OTHER, +}; + +/// max number of channels in the 2.4 GHZ band +#define MAC_DOMAINCHANNEL_24G_MAX 14 + +/// max number of channels in the 5 GHZ band +#define MAC_DOMAINCHANNEL_5G_MAX 28 + +/// Channel Flag +enum mac_chan_flags { + /// Cannot initiate radiation on this channel + CHAN_NO_IR = BIT(0), + /// Channel is not allowed + CHAN_DISABLED = BIT(1), + /// Radar detection required on this channel + CHAN_RADAR = BIT(2), +}; + +/// Primary Channel definition +struct mac_chan_def { + /// Frequency of the channel (in MHz) + u16 freq; + /// RF band (@ref mac_chan_band) + u8 band; + /// Additional information (@ref mac_chan_flags) + u8 flags; + /// Max transmit power allowed on this channel (dBm) + s8 tx_power; +}; + +/// Operating Channel +struct mac_chan_op { + /// Band (@ref mac_chan_band) + u8 band; + /// Channel type (@ref mac_chan_bandwidth) + u8 type; + /// Frequency for Primary 20MHz channel (in MHz) + u16 prim20_freq; + /// Frequency center of the contiguous channel or center of Primary 80+80 + /// (in MHz) + u16 center1_freq; + /// Frequency center of the non-contiguous secondary 80+80 (in MHz) + u16 center2_freq; + /// Max transmit power allowed on this channel (dBm) + s8 tx_power; + /// Additional information (@ref mac_chan_flags) + u8 flags; +}; + +/// Cipher suites (order is important as it is used by MACHW) +enum mac_cipher_suite { + /// 00-0F-AC 1 + MAC_CIPHER_WEP40 = 0, + /// 00-0F-AC 2 + MAC_CIPHER_TKIP = 1, + /// 00-0F-AC 4 + MAC_CIPHER_CCMP = 2, + /// 00-0F-AC 5 + MAC_CIPHER_WEP104 = 3, + /// 00-14-72 1 + MAC_CIPHER_WPI_SMS4 = 4, + /// 00-0F-AC 6 (aka AES_CMAC) + MAC_CIPHER_BIP_CMAC_128 = 5, + + // following cipher are not supported by MACHW + /// 00-0F-AC 08 + MAC_CIPHER_GCMP_128, + /// 00-0F-AC 09 + MAC_CIPHER_GCMP_256, + /// 00-0F-AC 10 + MAC_CIPHER_CCMP_256, + /// 00-0F-AC 11 + MAC_CIPHER_BIP_GMAC_128, + /// 00-0F-AC 12 + MAC_CIPHER_BIP_GMAC_256, + /// 00-0F-AC 13 + MAC_CIPHER_BIP_CMAC_256, + + MAC_CIPHER_INVALID = 0xFF +}; + +/// Authentication and Key Management suite +enum mac_akm_suite { + /// No security + MAC_AKM_NONE, + /// Pre RSN (WEP or WPA) + MAC_AKM_PRE_RSN, + /// 00-0F-AC 1 + MAC_AKM_8021X, + /// 00-0F-AC 2 + MAC_AKM_PSK, + /// 00-0F-AC 3 + MAC_AKM_FT_8021X, + /// 00-0F-AC 4 + MAC_AKM_FT_PSK, + /// 00-0F-AC 5 + MAC_AKM_8021X_SHA256, + /// 00-0F-AC 6 + MAC_AKM_PSK_SHA256, + /// 00-0F-AC 7 + MAC_AKM_TDLS, + /// 00-0F-AC 8 + MAC_AKM_SAE, + /// 00-0F-AC 9 + MAC_AKM_FT_OVER_SAE, + /// 00-0F-AC 11 + MAC_AKM_8021X_SUITE_B, + /// 00-0F-AC 12 + MAC_AKM_8021X_SUITE_B_192, + /// 00-0F-AC 14 + MAC_AKM_FILS_SHA256, + /// 00-0F-AC 15 + MAC_AKM_FILS_SHA384, + /// 00-0F-AC 16 + MAC_AKM_FT_FILS_SHA256, + /// 00-0F-AC 17 + MAC_AKM_FT_FILS_SHA384, + /// 00-0F-AC 18 + MAC_AKM_OWE, + + /// 00-14-72 1 + MAC_AKM_WAPI_CERT, + /// 00-14-72 2 + MAC_AKM_WAPI_PSK, +}; + +/// Scan result element, parsed from beacon or probe response frames. +struct mac_scan_result { + /// Scan result is valid + bool valid_flag; + /// Network BSSID. + struct mac_addr bssid; + /// Network name. + struct mac_ssid ssid; + /// Network type (@ref mac_bss_type). + u16 bsstype; + /// Network channel. + struct mac_chan_def *chan; + /// Network beacon period (in TU). + u16 beacon_period; + /// Capability information + u16 cap_info; + /// Supported AKM (bit-field of @ref mac_akm_suite) + u32 akm; + /// Group cipher (bit-field of @ref mac_cipher_suite) + u16 group_cipher; + /// Group cipher (bit-field of @ref mac_cipher_suite) + u16 pairwise_cipher; + /// RSSI of the scanned BSS (in dBm) + s8 rssi; +}; + +/// Legacy rate 802.11 definitions +enum mac_legacy_rates { + /// DSSS/CCK 1Mbps + MAC_RATE_1MBPS = 2, + /// DSSS/CCK 2Mbps + MAC_RATE_2MBPS = 4, + /// DSSS/CCK 5.5Mbps + MAC_RATE_5_5MBPS = 11, + /// OFDM 6Mbps + MAC_RATE_6MBPS = 12, + /// OFDM 9Mbps + MAC_RATE_9MBPS = 18, + /// DSSS/CCK 11Mbps + MAC_RATE_11MBPS = 22, + /// OFDM 12Mbps + MAC_RATE_12MBPS = 24, + /// OFDM 18Mbps + MAC_RATE_18MBPS = 36, + /// OFDM 24Mbps + MAC_RATE_24MBPS = 48, + /// OFDM 36Mbps + MAC_RATE_36MBPS = 72, + /// OFDM 48Mbps + MAC_RATE_48MBPS = 96, + /// OFDM 54Mbps + MAC_RATE_54MBPS = 108 +}; + +/// BSS Membership Selector definitions +enum mac_bss_membership { + /// HT PHY + MAC_BSS_MEMBERSHIP_HT_PHY = 127, + /// VHT PHY + MAC_BSS_MEMBERSHIP_VHT_PHY = 126, +}; + +/// MAC rateset maximum length +#define MAC_RATESET_LEN 12 + +/// Structure containing the legacy rateset of a station +struct mac_rateset { + /// Number of legacy rates supported + u8 length; + /// Array of legacy rates + u8 array[MAC_RATESET_LEN]; +}; + +/// MAC Security Key maximum length +#define MAC_SEC_KEY_LEN 32 // TKIP keys 256 bits (max length) with MIC keys + +/// Structure defining a security key +struct mac_sec_key { + /// Key material length + u8 length; + /// Key material + u32 array[MAC_SEC_KEY_LEN / 4]; +}; + +/// Access Category enumeration +enum mac_ac { + /// Background + AC_BK = 0, + /// Best-effort + AC_BE, + /// Video + AC_VI, + /// Voice + AC_VO, + /// Number of access categories + AC_MAX +}; + +/// Traffic ID enumeration +enum mac_tid { + /// TID_0. Mapped to @ref AC_BE as per 802.11 standard. + TID_0, + /// TID_1. Mapped to @ref AC_BK as per 802.11 standard. + TID_1, + /// TID_2. Mapped to @ref AC_BK as per 802.11 standard. + TID_2, + /// TID_3. Mapped to @ref AC_BE as per 802.11 standard. + TID_3, + /// TID_4. Mapped to @ref AC_VI as per 802.11 standard. + TID_4, + /// TID_5. Mapped to @ref AC_VI as per 802.11 standard. + TID_5, + /// TID_6. Mapped to @ref AC_VO as per 802.11 standard. + TID_6, + /// TID_7. Mapped to @ref AC_VO as per 802.11 standard. + TID_7, + /// Non standard Management TID used internally + TID_MGT, + /// Number of TID supported + TID_MAX +}; + +/// MCS bitfield maximum size (in bytes) +#define MAX_MCS_LEN 16 // 16 * 8 = 128 + +/// MAC HT capability information element +struct mac_htcapability { + /// HT capability information + __le16 ht_capa_info; + /// A-MPDU parameters + u8 a_mpdu_param; + /// Supported MCS + u8 mcs_rate[MAX_MCS_LEN]; + /// HT extended capability information + __le16 ht_extended_capa; + /// Beamforming capability information + __le32 tx_beamforming_capa; + /// Antenna selection capability information + u8 asel_capa; +}; + +/// MAC VHT capability information element +struct mac_vhtcapability { + /// VHT capability information + __le32 vht_capa_info; + /// RX MCS map + __le16 rx_mcs_map; + /// RX highest data rate + __le16 rx_highest; + /// TX MCS map + __le16 tx_mcs_map; + /// TX highest data rate + __le16 tx_highest; +}; + +/// Length (in bytes) of the MAC HE capability field +#define MAC_HE_MAC_CAPA_LEN 6 +/// Length (in bytes) of the PHY HE capability field +#define MAC_HE_PHY_CAPA_LEN 11 +/// Maximum length (in bytes) of the PPE threshold data +#define MAC_HE_PPE_THRES_MAX_LEN 25 + +/// Structure listing the per-NSS, per-BW supported MCS combinations +struct mac_he_mcs_nss_supp { + /// per-NSS supported MCS in RX, for BW <= 80MHz + __le16 rx_mcs_80; + /// per-NSS supported MCS in TX, for BW <= 80MHz + __le16 tx_mcs_80; + /// per-NSS supported MCS in RX, for BW = 160MHz + __le16 rx_mcs_160; + /// per-NSS supported MCS in TX, for BW = 160MHz + __le16 tx_mcs_160; + /// per-NSS supported MCS in RX, for BW = 80+80MHz + __le16 rx_mcs_80p80; + /// per-NSS supported MCS in TX, for BW = 80+80MHz + __le16 tx_mcs_80p80; +}; + +/// MAC HE capability information element +struct mac_hecapability { + /// MAC HE capabilities + u8 mac_cap_info[MAC_HE_MAC_CAPA_LEN]; + /// PHY HE capabilities + u8 phy_cap_info[MAC_HE_PHY_CAPA_LEN]; + /// Supported MCS combinations + struct mac_he_mcs_nss_supp mcs_supp; + /// PPE Thresholds data + u8 ppe_thres[MAC_HE_PPE_THRES_MAX_LEN]; +}; + +/// Station flags +enum mac_sta_flags { + /// Bit indicating that a STA has QoS (WMM) capability + STA_QOS_CAPA = BIT(0), + /// Bit indicating that a STA has HT capability + STA_HT_CAPA = BIT(1), + /// Bit indicating that a STA has VHT capability + STA_VHT_CAPA = BIT(2), + /// Bit indicating that a STA has MFP capability + STA_MFP_CAPA = BIT(3), + /// Bit indicating that the STA included the Operation Notification IE + STA_OPMOD_NOTIF = BIT(4), + /// Bit indicating that a STA has HE capability + STA_HE_CAPA = BIT(5), +}; + +/// Connection flags +enum mac_connection_flags { + /// Flag indicating whether the control port is controlled by host or not + CONTROL_PORT_HOST = BIT(0), + /// Flag indicating whether the control port frame shall be sent unencrypted + CONTROL_PORT_NO_ENC = BIT(1), + /// Flag indicating whether HT and VHT shall be disabled or not + DISABLE_HT = BIT(2), + /// Flag indicating whether WPA or WPA2 authentication is in use + WPA_WPA2_IN_USE = BIT(3), + /// Flag indicating whether MFP is in use + MFP_IN_USE = BIT(4), + // Flag indicating Roam + REASSOCIATION = BIT(5), +}; + +#endif // LMAC_MAC_H_ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h b/drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h new file mode 100644 index 0000000000000..8253fd8152fd1 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/lmac_msg.h @@ -0,0 +1,3101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Main definitions for message exchanges with LMAC + * + **************************************************************************************** + */ + +#ifndef LMAC_MSG_H_ +#define LMAC_MSG_H_ + +#include "lmac_mac.h" + +/* Task identifiers for communication between LMAC and DRIVER */ +enum { + TASK_NONE = 0xff, + + // MAC Management task. + TASK_MM = 0, + // DEBUG task + TASK_DBG, + /// SCAN task + TASK_SCAN, + /// TDLS task + TASK_TDLS, + /// SCANU task + TASK_SCANU, + /// ME task + TASK_ME, + /// SM task + TASK_SM, + /// APM task + TASK_APM, + /// BAM task + TASK_BAM, + /// MESH task + TASK_MESH, + /// RXU task + TASK_RXU, + /// RM_task + TASK_RM, + /// TWT task + TASK_TWT, +#if defined CONFIG_RWNX_FULLMAC + // This is used to define the last task that is running on the EMB processor + TASK_LAST_EMB = TASK_TWT, +#else +#error "Need to define SOFTMAC or FULLMAC" +#endif + // nX API task + TASK_API, + TASK_MAX, +}; + +/// For MAC HW States copied from "hal_machw.h" +enum { + /// MAC HW IDLE State. + HW_IDLE = 0, + /// MAC HW RESERVED State. + HW_RESERVED, + /// MAC HW DOZE State. + HW_DOZE, + /// MAC HW ACTIVE State. + HW_ACTIVE +}; + +/// Power Save mode setting +enum mm_ps_mode_state { + MM_PS_MODE_OFF, + MM_PS_MODE_ON, + MM_PS_MODE_ON_DYN, +}; + +/// Status/error codes used in the MAC software. +enum { + CO_OK, + CO_FAIL, + CO_EMPTY, + CO_FULL, + CO_BAD_PARAM, + CO_NOT_FOUND, + CO_NO_MORE_ELT_AVAILABLE, + CO_NO_ELT_IN_USE, + CO_BUSY, + CO_OP_IN_PROGRESS, +}; + +/// Remain on channel operation codes +enum mm_remain_on_channel_op { + MM_ROC_OP_START = 0, + MM_ROC_OP_CANCEL, +}; + +#define DRV_TASK_ID 100 + +/// Message Identifier. The number of messages is limited to 0xFFFF. +/// The message ID is divided in two parts: +/// - bits[15..10] : task index (no more than 64 tasks supported). +/// - bits[9..0] : message index (no more that 1024 messages per task). +typedef u16 lmac_msg_id_t; + +typedef u16 lmac_task_id_t; + +/// Build the first message ID of a task. +#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10)) + +#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10)) +#define MSG_I(msg) ((msg) & ((1 << 10) - 1)) + +/// Message structure. +struct lmac_msg { + lmac_msg_id_t id; ///< Message id. + lmac_task_id_t dest_id; ///< Destination kernel identifier. + lmac_task_id_t src_id; ///< Source kernel identifier. + u16 param_len; ///< Parameter embedded struct length. + u32 param[]; ///< Parameter embedded struct. Must be word-aligned. +}; + +/// List of messages related to the task. +enum mm_msg_tag { + /// RESET Request. + MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM), + /// RESET Confirmation. + MM_RESET_CFM, + /// START Request. + MM_START_REQ, + /// START Confirmation. + MM_START_CFM, + /// Read Version Request. + MM_VERSION_REQ, + /// Read Version Confirmation. + MM_VERSION_CFM, + /// ADD INTERFACE Request. + MM_ADD_IF_REQ, + /// ADD INTERFACE Confirmation. + MM_ADD_IF_CFM, + /// REMOVE INTERFACE Request. + MM_REMOVE_IF_REQ, + /// REMOVE INTERFACE Confirmation. + MM_REMOVE_IF_CFM, + /// STA ADD Request. + MM_STA_ADD_REQ, + /// STA ADD Confirm. + MM_STA_ADD_CFM, + /// STA DEL Request. + MM_STA_DEL_REQ, + /// STA DEL Confirm. + MM_STA_DEL_CFM, + /// RX FILTER CONFIGURATION Request. + MM_SET_FILTER_REQ, + /// RX FILTER CONFIGURATION Confirmation. + MM_SET_FILTER_CFM, + /// CHANNEL CONFIGURATION Request. + MM_SET_CHANNEL_REQ, + /// CHANNEL CONFIGURATION Confirmation. + MM_SET_CHANNEL_CFM, + /// DTIM PERIOD CONFIGURATION Request. + MM_SET_DTIM_REQ, + /// DTIM PERIOD CONFIGURATION Confirmation. + MM_SET_DTIM_CFM, + /// BEACON INTERVAL CONFIGURATION Request. + MM_SET_BEACON_INT_REQ, + /// BEACON INTERVAL CONFIGURATION Confirmation. + MM_SET_BEACON_INT_CFM, + /// BASIC RATES CONFIGURATION Request. + MM_SET_BASIC_RATES_REQ, + /// BASIC RATES CONFIGURATION Confirmation. + MM_SET_BASIC_RATES_CFM, + /// BSSID CONFIGURATION Request. + MM_SET_BSSID_REQ, + /// BSSID CONFIGURATION Confirmation. + MM_SET_BSSID_CFM, + /// EDCA PARAMETERS CONFIGURATION Request. + MM_SET_EDCA_REQ, + /// EDCA PARAMETERS CONFIGURATION Confirmation. + MM_SET_EDCA_CFM, + /// ABGN MODE CONFIGURATION Request. + MM_SET_MODE_REQ, + /// ABGN MODE CONFIGURATION Confirmation. + MM_SET_MODE_CFM, + /// Request setting the VIF active state (i.e associated or AP started) + MM_SET_VIF_STATE_REQ, + /// Confirmation of the @ref MM_SET_VIF_STATE_REQ message. + MM_SET_VIF_STATE_CFM, + /// SLOT TIME PARAMETERS CONFIGURATION Request. + MM_SET_SLOTTIME_REQ, + /// SLOT TIME PARAMETERS CONFIGURATION Confirmation. + MM_SET_SLOTTIME_CFM, + /// Power Mode Change Request. + MM_SET_IDLE_REQ, + /// Power Mode Change Confirm. + MM_SET_IDLE_CFM, + /// KEY ADD Request. + MM_KEY_ADD_REQ, + /// KEY ADD Confirm. + MM_KEY_ADD_CFM, + /// KEY DEL Request. + MM_KEY_DEL_REQ, + /// KEY DEL Confirm. + MM_KEY_DEL_CFM, + /// Block Ack agreement info addition + MM_BA_ADD_REQ, + /// Block Ack agreement info addition confirmation + MM_BA_ADD_CFM, + /// Block Ack agreement info deletion + MM_BA_DEL_REQ, + /// Block Ack agreement info deletion confirmation + MM_BA_DEL_CFM, + /// Indication of the primary TBTT to the upper MAC. Upon the reception of + /// this + // message the upper MAC has to push the beacon(s) to the beacon + // transmission queue. + MM_PRIMARY_TBTT_IND, + /// Indication of the secondary TBTT to the upper MAC. Upon the reception of + /// this + // message the upper MAC has to push the beacon(s) to the beacon + // transmission queue. + MM_SECONDARY_TBTT_IND, + /// Request for changing the TX power + MM_SET_POWER_REQ, + /// Confirmation of the TX power change + MM_SET_POWER_CFM, + /// Request to the LMAC to trigger the embedded logic analyzer and forward + /// the debug + /// dump. + MM_DBG_TRIGGER_REQ, + /// Set Power Save mode + MM_SET_PS_MODE_REQ, + /// Set Power Save mode confirmation + MM_SET_PS_MODE_CFM, + /// Request to add a channel context + MM_CHAN_CTXT_ADD_REQ, + /// Confirmation of the channel context addition + MM_CHAN_CTXT_ADD_CFM, + /// Request to delete a channel context + MM_CHAN_CTXT_DEL_REQ, + /// Confirmation of the channel context deletion + MM_CHAN_CTXT_DEL_CFM, + /// Request to link a channel context to a VIF + MM_CHAN_CTXT_LINK_REQ, + /// Confirmation of the channel context link + MM_CHAN_CTXT_LINK_CFM, + /// Request to unlink a channel context from a VIF + MM_CHAN_CTXT_UNLINK_REQ, + /// Confirmation of the channel context unlink + MM_CHAN_CTXT_UNLINK_CFM, + /// Request to update a channel context + MM_CHAN_CTXT_UPDATE_REQ, + /// Confirmation of the channel context update + MM_CHAN_CTXT_UPDATE_CFM, + /// Request to schedule a channel context + MM_CHAN_CTXT_SCHED_REQ, + /// Confirmation of the channel context scheduling + MM_CHAN_CTXT_SCHED_CFM, + /// Request to change the beacon template in LMAC + MM_BCN_CHANGE_REQ, + /// Confirmation of the beacon change + MM_BCN_CHANGE_CFM, + /// Request to update the TIM in the beacon (i.e to indicate traffic + /// bufferized at AP) + MM_TIM_UPDATE_REQ, + /// Confirmation of the TIM update + MM_TIM_UPDATE_CFM, + /// Connection loss indication + MM_CONNECTION_LOSS_IND, + /// Channel context switch indication to the upper layers + MM_CHANNEL_SWITCH_IND, + /// Channel context pre-switch indication to the upper layers + MM_CHANNEL_PRE_SWITCH_IND, + /// Request to remain on channel or cancel remain on channel + MM_REMAIN_ON_CHANNEL_REQ, + /// Confirmation of the (cancel) remain on channel request + MM_REMAIN_ON_CHANNEL_CFM, + /// Remain on channel expired indication + MM_REMAIN_ON_CHANNEL_EXP_IND, + /// Indication of a PS state change of a peer device + MM_PS_CHANGE_IND, + /// Indication that some buffered traffic should be sent to the peer device + MM_TRAFFIC_REQ_IND, + /// Request to modify the STA Power-save mode options + MM_SET_PS_OPTIONS_REQ, + /// Confirmation of the PS options setting + MM_SET_PS_OPTIONS_CFM, + /// Indication of PS state change for a P2P VIF + MM_P2P_VIF_PS_CHANGE_IND, + /// Indication that CSA counter has been updated + MM_CSA_COUNTER_IND, + /// Channel occupation report indication + MM_CHANNEL_SURVEY_IND, + /// Message containing Beamformer Information + MM_BFMER_ENABLE_REQ, + /// Request to Start/Stop/Update NOA - GO Only + MM_SET_P2P_NOA_REQ, + /// Request to Start/Stop/Update Opportunistic PS - GO Only + MM_SET_P2P_OPPPS_REQ, + /// Start/Stop/Update NOA Confirmation + MM_SET_P2P_NOA_CFM, + /// Start/Stop/Update Opportunistic PS Confirmation + MM_SET_P2P_OPPPS_CFM, + /// P2P NoA Update Indication - GO Only + MM_P2P_NOA_UPD_IND, + /// Request to set RSSI threshold and RSSI hysteresis + MM_CFG_RSSI_REQ, + /// Indication that RSSI level is below or above the threshold + MM_RSSI_STATUS_IND, + /// Indication that CSA is done + MM_CSA_FINISH_IND, + /// Indication that CSA is in prorgess (resp. done) and traffic must be + /// stopped (resp. restarted) + MM_CSA_TRAFFIC_IND, + /// Request to update the group information of a station + MM_MU_GROUP_UPDATE_REQ, + /// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message + MM_MU_GROUP_UPDATE_CFM, + /// Request to initialize the antenna diversity algorithm + MM_ANT_DIV_INIT_REQ, + /// Request to stop the antenna diversity algorithm + MM_ANT_DIV_STOP_REQ, + /// Request to update the antenna switch status + MM_ANT_DIV_UPDATE_REQ, + /// Request to switch the antenna connected to path_0 + MM_SWITCH_ANTENNA_REQ, + /// Indication that a packet loss has occurred + MM_PKTLOSS_IND, + + MM_SET_ARPOFFLOAD_REQ, + MM_SET_ARPOFFLOAD_CFM, + MM_SET_AGG_DISABLE_REQ, + MM_SET_AGG_DISABLE_CFM, + MM_SET_COEX_REQ, + MM_SET_COEX_CFM, + MM_SET_RF_CONFIG_REQ, + MM_SET_RF_CONFIG_CFM, + MM_SET_RF_CALIB_REQ, + MM_SET_RF_CALIB_CFM, + + /// MU EDCA PARAMETERS Configuration Request. + MM_SET_MU_EDCA_REQ, + /// MU EDCA PARAMETERS Configuration Confirmation. + MM_SET_MU_EDCA_CFM, + /// UORA PARAMETERS Configuration Request. + MM_SET_UORA_REQ, + /// UORA PARAMETERS Configuration Confirmation. + MM_SET_UORA_CFM, + /// TXOP RTS THRESHOLD Configuration Request. + MM_SET_TXOP_RTS_THRES_REQ, + /// TXOP RTS THRESHOLD Configuration Confirmation. + MM_SET_TXOP_RTS_THRES_CFM, + /// HE BSS Color Configuration Request. + MM_SET_BSS_COLOR_REQ, + /// HE BSS Color Configuration Confirmation. + MM_SET_BSS_COLOR_CFM, + + MM_GET_MAC_ADDR_REQ, + MM_GET_MAC_ADDR_CFM, + + MM_GET_STA_INFO_REQ, + MM_GET_STA_INFO_CFM, + + MM_SET_TXPWR_IDX_LVL_REQ, + MM_SET_TXPWR_IDX_LVL_CFM, + + MM_SET_TXPWR_OFST_REQ, + MM_SET_TXPWR_OFST_CFM, + + MM_SET_STACK_START_REQ, + MM_SET_STACK_START_CFM, + + MM_APM_STALOSS_IND, + + MM_SET_VENDOR_HWCONFIG_REQ, + MM_SET_VENDOR_HWCONFIG_CFM, + + MM_GET_FW_VERSION_REQ, + MM_GET_FW_VERSION_CFM, + + MM_SET_RESUME_RESTORE_REQ, + MM_SET_RESUME_RESTORE_CFM, + + MM_GET_WIFI_DISABLE_REQ, + MM_GET_WIFI_DISABLE_CFM, + + MM_CFG_RSSI_CFM, + + MM_SET_VENDOR_SWCONFIG_REQ, + MM_SET_VENDOR_SWCONFIG_CFM, + + MM_SET_TXPWR_LVL_ADJ_REQ, + MM_SET_TXPWR_LVL_ADJ_CFM, + + MM_RADAR_DETECT_IND, + + MM_SET_APF_PROG_REQ, + MM_SET_APF_PROG_CFM, + + MM_GET_APF_PROG_REQ, + MM_GET_APF_PROG_CFM, + + /// arp announcement frame reception indication + MM_ARP_ANNOUNCEMENT_IND, + + /// MAX number of messages + MM_MAX, +}; + +/// Interface types +enum { + /// ESS STA interface + MM_STA, + /// IBSS STA interface + MM_IBSS, + /// AP interface + MM_AP, + // Mesh Point interface + MM_MESH_POINT, + // Monitor interface + MM_MONITOR, +}; + +/// BA agreement types +enum { + /// BlockAck agreement for TX + BA_AGMT_TX, + /// BlockAck agreement for RX + BA_AGMT_RX, +}; + +/// BA agreement related status +enum { + /// Correct BA agreement establishment + BA_AGMT_ESTABLISHED, + /// BA agreement already exists for STA+TID requested, cannot override it + /// (should have been deleted first) + BA_AGMT_ALREADY_EXISTS, + /// Correct BA agreement deletion + BA_AGMT_DELETED, + /// BA agreement for the (STA, TID) doesn't exist so nothing to delete + BA_AGMT_DOESNT_EXIST, +}; + +/// Features supported by LMAC - Positions +enum mm_features { + /// Beaconing + MM_FEAT_BCN_BIT = 0, + /* + * /// Autonomous Beacon Transmission + * MM_FEAT_AUTOBCN_BIT, + * /// Scan in LMAC + * MM_FEAT_HWSCAN_BIT, + * /// Connection Monitoring + * MM_FEAT_CMON_BIT, + * /// Multi Role + * MM_FEAT_MROLE_BIT, + */ + /// Radar Detection + MM_FEAT_RADAR_BIT, + /// Power Save + MM_FEAT_PS_BIT, + /// UAPSD + MM_FEAT_UAPSD_BIT, + /// DPSM + // MM_FEAT_DPSM_BIT, + /// A-MPDU + MM_FEAT_AMPDU_BIT, + /// A-MSDU + MM_FEAT_AMSDU_BIT, + /// Channel Context + // MM_FEAT_CHNL_CTXT_BIT, + /// Packet reordering + // MM_FEAT_REORD_BIT, + /// P2P + MM_FEAT_P2P_BIT, + /// P2P Go + MM_FEAT_P2P_GO_BIT, + /// UMAC Present + MM_FEAT_UMAC_BIT, + /// VHT support + MM_FEAT_VHT_BIT, + /// Beamformee + MM_FEAT_BFMEE_BIT, + /// Beamformer + MM_FEAT_BFMER_BIT, + /// WAPI + MM_FEAT_WAPI_BIT, + /// MFP + MM_FEAT_MFP_BIT, + /// Mu-MIMO RX support + MM_FEAT_MU_MIMO_RX_BIT, + /// Mu-MIMO TX support + MM_FEAT_MU_MIMO_TX_BIT, + /// Wireless Mesh Networking + MM_FEAT_MESH_BIT, + /// TDLS support + MM_FEAT_TDLS_BIT, + /// Antenna Diversity support + MM_FEAT_ANT_DIV_BIT, + /// UF support + MM_FEAT_UF_BIT, + /// A-MSDU maximum size (bit0) + MM_AMSDU_MAX_SIZE_BIT0, + /// A-MSDU maximum size (bit1) + MM_AMSDU_MAX_SIZE_BIT1, + /// MON_DATA support + MM_FEAT_MON_DATA_BIT, + /// HE (802.11ax) support + MM_FEAT_HE_BIT, + /// TWT support + MM_FEAT_TWT_BIT, +}; + +/// Maximum number of words in the configuration buffer +#define PHY_CFG_BUF_SIZE 16 + +/// Structure containing the parameters of the PHY configuration +struct phy_cfg_tag { + /// Buffer containing the parameters specific for the PHY used + u32 parameters[PHY_CFG_BUF_SIZE]; +}; + +/// Structure containing the parameters of the Trident PHY configuration +struct phy_trd_cfg_tag { + /// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble) + u8 path_mapping; + /// TX DC offset compensation + u32 tx_dc_off_comp; +}; + +/// Structure containing the parameters of the Karst PHY configuration +struct phy_karst_cfg_tag { + /// TX IQ mismatch compensation in 2.4GHz + u32 tx_iq_comp_2_4G[2]; + /// RX IQ mismatch compensation in 2.4GHz + u32 rx_iq_comp_2_4G[2]; + /// TX IQ mismatch compensation in 5GHz + u32 tx_iq_comp_5G[2]; + /// RX IQ mismatch compensation in 5GHz + u32 rx_iq_comp_5G[2]; + /// RF path used by default (0 or 1) + u8 path_used; +}; + +/// Structure containing the parameters of the @ref MM_START_REQ message +struct mm_start_req { + /// PHY configuration + struct phy_cfg_tag phy_cfg; + /// UAPSD timeout + u32 uapsd_timeout; + /// Local LP clock accuracy (in ppm) + u16 lp_clk_accuracy; +}; + +/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message +struct mm_set_channel_req { + /// Channel information + struct mac_chan_op chan; + /// Index of the RF for which the channel has to be set (0: operating + /// (primary), 1: secondary RF (used for additional radar detection). This + /// parameter is reserved if no secondary RF is available in the system + u8 index; +}; + +/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message +struct mm_set_channel_cfm { + /// Radio index to be used in policy table + u8 radio_idx; + /// TX power configured (in dBm) + s8 power; +}; + +/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message +struct mm_set_dtim_req { + /// DTIM period + u8 dtim_period; +}; + +/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message +struct mm_set_power_req { + /// Index of the interface for which the parameter is configured + u8 inst_nbr; + /// TX power (in dBm) + s8 power; +}; + +/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message +struct mm_set_power_cfm { + /// Radio index to be used in policy table + u8 radio_idx; + /// TX power configured (in dBm) + s8 power; +}; + +/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ +/// message +struct mm_set_beacon_int_req { + /// Beacon interval + u16 beacon_int; + /// Index of the interface for which the parameter is configured + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ +/// message +struct mm_set_basic_rates_req { + /// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW + /// register) + u32 rates; + /// Index of the interface for which the parameter is configured + u8 inst_nbr; + /// Band on which the interface will operate + u8 band; +}; + +/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message +struct mm_set_bssid_req { + /// BSSID to be configured in HW + struct mac_addr bssid; + /// Index of the interface for which the parameter is configured + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message +struct mm_set_filter_req { + /// RX filter to be put into rxCntrlReg HW register + u32 filter; +}; + +/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message. +struct mm_add_if_req { + /// Type of the interface (AP, STA, ADHOC, ...) + u8 type; + /// MAC ADDR of the interface to start + struct mac_addr addr; + /// P2P Interface + bool p2p; +}; + +/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message +struct mm_set_edca_req { + /// EDCA parameters of the queue (as expected by edcaACxReg HW register) + u32 ac_param; + /// Flag indicating if UAPSD can be used on this queue + bool uapsd; + /// HW queue for which the parameters are configured + u8 hw_queue; + /// Index of the interface for which the parameters are configured + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_SET_MU_EDCA_REQ message +struct mm_set_mu_edca_req { + /// MU EDCA parameters of the different HE queues + u32 param[AC_MAX]; +}; + +/// Structure containing the parameters of the @ref MM_SET_UORA_REQ message +struct mm_set_uora_req { + /// Minimum exponent of OFDMA Contention Window. + u8 eocw_min; + /// Maximum exponent of OFDMA Contention Window. + u8 eocw_max; +}; + +/// Structure containing the parameters of the @ref MM_SET_TXOP_RTS_THRES_REQ +/// message +struct mm_set_txop_rts_thres_req { + /// TXOP RTS threshold + u16 txop_dur_rts_thres; + /// Index of the interface for which the parameter is configured + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_SET_BSS_COLOR_REQ message +struct mm_set_bss_color_req { + /// HE BSS color, formatted as per BSS_COLOR MAC HW register + u32 bss_color; +}; + +struct mm_set_idle_req { + u8 hw_idle; +}; + +/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message +struct mm_set_slottime_req { + /// Slot time expressed in us + u8 slottime; +}; + +/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message +struct mm_set_mode_req { + /// abgnMode field of macCntrl1Reg register + u8 abgnmode; +}; + +/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message +struct mm_set_vif_state_req { + /// Association Id received from the AP (valid only if the VIF is of STA + /// type) + __le16 aid; + /// Flag indicating if the VIF is active or not + bool active; + /// Interface index + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message. +struct mm_add_if_cfm { + /// Status of operation (different from 0 if unsuccessful) + u8 status; + /// Interface index assigned by the LMAC + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message. +struct mm_remove_if_req { + /// Interface index assigned by the LMAC + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_VERSION_CFM message. +struct mm_version_cfm { + /// Version of the LMAC FW + u32 version_lmac; + /// Version1 of the MAC HW (as encoded in version1Reg MAC HW register) + u32 version_machw_1; + /// Version2 of the MAC HW (as encoded in version2Reg MAC HW register) + u32 version_machw_2; + /// Version1 of the PHY (depends on actual PHY) + u32 version_phy_1; + /// Version2 of the PHY (depends on actual PHY) + u32 version_phy_2; + /// Supported Features + u32 features; + /// Maximum number of supported stations + u16 max_sta_nb; + /// Maximum number of supported virtual interfaces + u8 max_vif_nb; +}; + +/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message. +struct mm_sta_add_req { + /// Bitfield showing some capabilities of the STA (@ref enum mac_sta_flags) + u32 capa_flags; + /// Maximum A-MPDU size, in bytes, for HE frames + u32 ampdu_size_max_he; + /// Maximum A-MPDU size, in bytes, for VHT frames + u32 ampdu_size_max_vht; + /// PAID/GID + u32 paid_gid; + /// Maximum A-MPDU size, in bytes, for HT frames + u16 ampdu_size_max_ht; + /// MAC address of the station to be added + struct mac_addr mac_addr; + /// A-MPDU spacing, in us + u8 ampdu_spacing_min; + /// Interface index + u8 inst_nbr; + /// TDLS station + bool tdls_sta; + /// Indicate if the station is TDLS link initiator station + bool tdls_sta_initiator; + /// Indicate if the TDLS Channel Switch is allowed + bool tdls_chsw_allowed; + /// nonTransmitted BSSID index, set to the BSSID index in case the STA added + /// is an AP that is a nonTransmitted BSSID. Should be set to 0 otherwise + u8 bssid_index; + /// Maximum BSSID indicator, valid if the STA added is an AP that is a + /// nonTransmitted BSSID + u8 max_bssid_ind; +}; + +/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message. +struct mm_sta_add_cfm { + /// Status of the operation (different from 0 if unsuccessful) + u8 status; + /// Index assigned by the LMAC to the newly added station + u8 sta_idx; + /// MAC HW index of the newly added station + u8 hw_sta_idx; +}; + +/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message. +struct mm_sta_del_req { + /// Index of the station to be deleted + u8 sta_idx; +}; + +/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message. +struct mm_sta_del_cfm { + /// Status of the operation (different from 0 if unsuccessful) + u8 status; +}; + +/// Structure containing the parameters of the SET_POWER_MODE REQ message. +struct mm_setpowermode_req { + u8 mode; + u8 sta_idx; +}; + +/// Structure containing the parameters of the SET_POWER_MODE CFM message. +struct mm_setpowermode_cfm { + u8 status; +}; + +/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message. +struct mm_key_add_req { + /// Key index (valid only for default keys) + u8 key_idx; + /// STA index (valid only for pairwise or mesh group keys) + u8 sta_idx; + /// Key material + struct mac_sec_key key; + /// Cipher suite (WEP64, WEP128, TKIP, CCMP) + u8 cipher_suite; + /// Index of the interface for which the key is set (valid only for default + /// keys or mesh group keys) + u8 inst_nbr; + /// A-MSDU SPP parameter + u8 spp; + /// Indicate if provided key is a pairwise key or not + bool pairwise; +}; + +/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message. +struct mm_key_add_cfm { + /// Status of the operation (different from 0 if unsuccessful) + u8 status; + /// HW index of the key just added + u8 hw_key_idx; +}; + +/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message. +struct mm_key_del_req { + /// HW index of the key to be deleted + u8 hw_key_idx; +}; + +/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message. +struct mm_ba_add_req { + /// Type of agreement (0: TX, 1: RX) + u8 type; + /// Index of peer station with which the agreement is made + u8 sta_idx; + /// TID for which the agreement is made with peer station + u8 tid; + /// Buffer size - number of MPDUs that can be held in its buffer per TID + u8 bufsz; + /// Start sequence number negotiated during BA setup - the one in first + /// aggregated MPDU counts more + u16 ssn; +}; + +/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message. +struct mm_ba_add_cfm { + /// Index of peer station for which the agreement is being confirmed + u8 sta_idx; + /// TID for which the agreement is being confirmed + u8 tid; + /// Status of ba establishment + u8 status; +}; + +/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message. +struct mm_ba_del_req { + /// Type of agreement (0: TX, 1: RX) + u8 type; + /// Index of peer station for which the agreement is being deleted + u8 sta_idx; + /// TID for which the agreement is being deleted + u8 tid; +}; + +/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message. +struct mm_ba_del_cfm { + /// Index of peer station for which the agreement deletion is being + /// confirmed + u8 sta_idx; + /// TID for which the agreement deletion is being confirmed + u8 tid; + /// Status of ba deletion + u8 status; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message +struct mm_chan_ctxt_add_req { + /// Operating channel + struct mac_chan_op chan; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message +struct mm_chan_ctxt_add_cfm { + /// Status of the addition + u8 status; + /// Index of the new channel context + u8 index; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message +struct mm_chan_ctxt_del_req { + /// Index of the new channel context to be deleted + u8 index; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ +/// message +struct mm_chan_ctxt_link_req { + /// VIF index + u8 vif_index; + /// Channel context index + u8 chan_index; + /// Indicate if this is a channel switch (unlink current ctx first if true) + u8 chan_switch; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ +/// message +struct mm_chan_ctxt_unlink_req { + /// VIF index + u8 vif_index; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ +/// message +struct mm_chan_ctxt_update_req { + /// Channel context index + u8 chan_index; + /// New channel information + struct mac_chan_op chan; +}; + +/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ +/// message +struct mm_chan_ctxt_sched_req { + /// VIF index + u8 vif_index; + /// Channel context index + u8 chan_index; + /// Type of the scheduling request (0: normal scheduling, 1: derogatory + /// scheduling) + u8 type; +}; + +/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND +/// message +struct mm_channel_switch_ind { + /// Index of the channel context we will switch to + u8 chan_index; + /// Indicate if the switch has been triggered by a Remain on channel request + bool roc; + /// VIF on which remain on channel operation has been started (if roc == 1) + u8 vif_index; + /// Indicate if the switch has been triggered by a TDLS Remain on channel + /// request + bool roc_tdls; +}; + +/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND +/// message +struct mm_channel_pre_switch_ind { + /// Index of the channel context we will switch to + u8 chan_index; +}; + +/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND +/// message. +struct mm_connection_loss_ind { + /// VIF instance number + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message. +struct mm_dbg_trigger_req { + /// Error trace to be reported by the LMAC + char error[64]; +}; + +/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message. +struct mm_set_ps_mode_req { + /// Power Save is activated or deactivated + u8 new_state; +}; + +/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message. +#define BCN_MAX_CSA_CPT 2 +struct mm_bcn_change_req { + /// Pointer, in host memory, to the new beacon template + u32 bcn_ptr; + /// Length of the beacon template + u16 bcn_len; + /// Offset of the TIM IE in the beacon + u16 tim_oft; + /// Length of the TIM IE + u8 tim_len; + /// Index of the VIF for which the beacon is updated + u8 inst_nbr; + /// Offset of CSA (channel switch announcement) counters (0 means no + /// counter) + u8 csa_oft[BCN_MAX_CSA_CPT]; +}; + +/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message. +struct mm_tim_update_req { + /// Association ID of the STA the bit of which has to be updated (0 for + /// BC/MC traffic) + u16 aid; + /// Flag indicating the availability of data packets for the given STA + u8 tx_avail; + /// Index of the VIF for which the TIM is updated + u8 inst_nbr; +}; + +/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ +/// message. +struct mm_remain_on_channel_req { + /// Operation Code + u8 op_code; + /// VIF Index + u8 vif_index; + /// Band (2.4GHz or 5GHz) + u8 band; + /// Channel type: 20,40,80,160 or 80+80 MHz + u8 type; + /// Frequency for Primary 20MHz channel (in MHz) + u16 prim20_freq; + /// Frequency for Center of the contiguous channel or center of Primary + /// 80+80 + u16 center1_freq; + /// Frequency for Center of the non-contiguous secondary 80+80 + u16 center2_freq; + /// Duration (in ms) + u32 duration_ms; + /// TX power (in dBm) + s8 tx_power; +}; + +/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM +/// message +struct mm_remain_on_channel_cfm { + /// Operation Code + u8 op_code; + /// Status of the operation + u8 status; + /// Channel Context index + u8 chan_ctxt_index; +}; + +/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND +/// message +struct mm_remain_on_channel_exp_ind { + /// VIF Index + u8 vif_index; + /// Channel Context index + u8 chan_ctxt_index; +}; + +/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ +/// message. +struct mm_set_uapsd_tmr_req { + /// action: Start or Stop the timer + u8 action; + /// timeout value, in milliseconds + u32 timeout; +}; + +/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM +/// message. +struct mm_set_uapsd_tmr_cfm { + /// Status of the operation (different from 0 if unsuccessful) + u8 status; +}; + +/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message +struct mm_ps_change_ind { + /// Index of the peer device that is switching its PS state + u8 sta_idx; + /// New PS state of the peer device (0: active, 1: sleeping) + u8 ps_state; +}; + +/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND +/// message +struct mm_p2p_vif_ps_change_ind { + /// Index of the P2P VIF that is switching its PS state + u8 vif_index; + /// New PS state of the P2P VIF interface (0: active, 1: sleeping) + u8 ps_state; +}; + +/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message +struct mm_traffic_req_ind { + /// Index of the peer device that needs traffic + u8 sta_idx; + /// Number of packets that need to be sent (if 0, all buffered traffic shall + /// be sent and if set to @ref PS_SP_INTERRUPTED, it means that current + /// service period has been interrupted) + u8 pkt_cnt; + /// Flag indicating if the traffic request concerns U-APSD queues or not + bool uapsd; +}; + +/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ +/// message. +struct mm_set_ps_options_req { + /// VIF Index + u8 vif_index; + /// Listen interval (0 if wake up shall be based on DTIM period) + u16 listen_interval; + /// Flag indicating if we shall listen the BC/MC traffic or not + bool dont_listen_bc_mc; +}; + +/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message +struct mm_csa_counter_ind { + /// Index of the VIF + u8 vif_index; + /// Updated CSA counter value + u8 csa_count; +}; + +/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND +/// message +struct mm_channel_survey_ind { + /// Frequency of the channel + u16 freq; + /// Noise in dbm + s8 noise_dbm; + /// Amount of time spent of the channel (in ms) + u32 chan_time_ms; + /// Amount of time the primary channel was sensed busy + u32 chan_time_busy_ms; +}; + +/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message. +struct mm_bfmer_enable_req { + /** + * Address of the beamforming report space allocated in host memory + * (Valid only if vht_su_bfmee is true) + */ + u32 host_bfr_addr; + /** + * Size of the beamforming report space allocated in host memory. This space + * should be twice the maximum size of the expected beamforming reports as + * the FW will divide it in two in order to be able to upload a new report + * while another one is used in transmission + */ + u16 host_bfr_size; + /// AID + u16 aid; + /// Station Index + u8 sta_idx; + /// Maximum number of spatial streams the station can receive + u8 rx_nss; + /** + * Indicate if peer STA is MU Beamformee (VHT) capable + * (Valid only if vht_su_bfmee is true) + */ + bool vht_mu_bfmee; +}; + +/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message. +struct mm_set_p2p_noa_req { + /// VIF Index + u8 vif_index; + /// Allocated NOA Instance Number - Valid only if count = 0 + u8 noa_inst_nb; + /// Count + u8 count; + /// Indicate if NoA can be paused for traffic reason + bool dyn_noa; + /// Duration (in us) + u32 duration_us; + /// Interval (in us) + u32 interval_us; + /// Start Time offset from next TBTT (in us) + u32 start_offset; +}; + +#ifdef AICWF_ARP_OFFLOAD +struct mm_set_arpoffload_en_req { + u32 ipaddr; + u8 enable; + u8 vif_idx; +}; + +struct mm_set_arpoffload_en_cfm { + u8 status; +}; +#endif + +struct mm_set_agg_disable_req { + u8 disable; + u8 staidx; + u8 disable_rx; +}; + +struct mm_set_coex_req { + u8 bt_on; + u8 disable_coexnull; + u8 enable_nullcts; + u8 enable_periodic_timer; + u8 coex_timeslot_set; + u32 coex_timeslot[2]; +}; + +struct mm_set_rf_config_req { + u8 table_sel; + u8 table_ofst; + u8 table_num; + u8 deft_page; + u32 data[64]; +}; + +struct mm_set_rf_calib_req { + u32 cal_cfg_24g; + u32 cal_cfg_5g; + u32 param_alpha; + u32 bt_calib_en; + u32 bt_calib_param; + u8 xtal_cap; + u8 xtal_cap_fine; +}; + +struct mm_set_rf_calib_cfm { + u32 rxgain_24g_addr; + u32 rxgain_5g_addr; + u32 txgain_24g_addr; + u32 txgain_5g_addr; +}; + +struct mm_get_mac_addr_req { + u32 get; +}; + +struct mm_get_mac_addr_cfm { + u8 mac_addr[6]; +}; + +struct mm_get_sta_info_req { + u8 sta_idx; +}; + +struct mm_get_sta_info_cfm { + u32 rate_info; + u32 txfailed; + u8 rssi; +}; + +#define REGION_NUM_MAX 5 + +struct txpwr_lvl_conf { + u8 enable; + u8 dsss; + u8 ofdmlowrate_2g4; + u8 ofdm64qam_2g4; + u8 ofdm256qam_2g4; + u8 ofdm1024qam_2g4; + u8 ofdmlowrate_5g; + u8 ofdm64qam_5g; + u8 ofdm256qam_5g; + u8 ofdm1024qam_5g; +}; + +struct txpwr_lvl_conf_v2 { + u8 enable; + s8 pwrlvl_11b_11ag_2g4[12]; + s8 pwrlvl_11n_11ac_2g4[10]; + s8 pwrlvl_11ax_2g4[12]; +}; + +struct txpwr_lvl_conf_v3 { + u8 enable; + s8 pwrlvl_11b_11ag_2g4[12]; + s8 pwrlvl_11n_11ac_2g4[10]; + s8 pwrlvl_11ax_2g4[12]; + s8 pwrlvl_11a_5g[12]; + s8 pwrlvl_11n_11ac_5g[10]; + s8 pwrlvl_11ax_5g[12]; +}; + +struct txpwr_lvl_adj_conf { + u8 enable; + s8 pwrlvl_adj_tbl_2g4[3]; + s8 pwrlvl_adj_tbl_5g[6]; +}; + +struct txpwr_loss_conf { + u8 loss_enable_2g4; + s8 loss_value_2g4; + u8 loss_enable_5g; + s8 loss_value_5g; +}; + +struct mm_set_txpwr_lvl_req { + union { + struct txpwr_lvl_conf txpwr_lvl; + struct txpwr_lvl_conf_v2 txpwr_lvl_v2; + struct txpwr_lvl_conf_v3 txpwr_lvl_v3; + }; +}; + +struct mm_set_txpwr_lvl_adj_req { + struct txpwr_lvl_adj_conf txpwr_lvl_adj; +}; + +struct txpwr_idx_conf { + u8 enable; + u8 dsss; + u8 ofdmlowrate_2g4; + u8 ofdm64qam_2g4; + u8 ofdm256qam_2g4; + u8 ofdm1024qam_2g4; + u8 ofdmlowrate_5g; + u8 ofdm64qam_5g; + u8 ofdm256qam_5g; + u8 ofdm1024qam_5g; +}; + +struct mm_set_txpwr_idx_req { + struct txpwr_idx_conf txpwr_idx; +}; + +struct txpwr_ofst_conf { + u8 enable; + s8 chan_1_4; + s8 chan_5_9; + s8 chan_10_13; + s8 chan_36_64; + s8 chan_100_120; + s8 chan_122_140; + s8 chan_142_165; +}; + +/* + * pwrofst2x_tbl_2g4[3][3]: + * +---------------+----------+----------+----------+ + * | RateTyp\ChGrp | CH_1_4 | CH_5_9 | CH_10_13 | + * +---------------+----------+----------+----------+ + * | DSSS | [0][0] | [0][1] | [0][2] | + * +---------------+----------+----------+----------+ + * | OFDM_HIGHRATE | [1][0] | [1][1] | [1][2] | + * +---------------+----------+----------+----------+ + * | OFDM_LOWRATE | [2][0] | [2][1] | [2][2] | + * +---------------+----------+----------+----------+ + * pwrofst2x_tbl_5g[3][6]: + * +---------------+--------------+--------------+----------------+----------------+--+--+ + * | RateTyp\ChGrp | CH_42(36~50) | CH_58(51~64) | CH_106(98~114) | + * CH_122(115~130)| CH_138(131~146)| CH_155(147~166)| + * +---------------+--------------+--------------+----------------+----------------+--+--+ + * | OFDM_LOWRATE | [0][0] | [0][1] | [0][2] | [0][3] + * | [0][4] | [0][5] | + * +---------------+--------------+--------------+----------------+----------------+--+--+ + * | OFDM_HIGHRATE | [1][0] | [1][1] | [1][2] | [1][3] + * | [1][4] | [1][5] | + * +---------------+--------------+--------------+----------------+----------------+--+--+ + * | OFDM_MIDRATE | [2][0] | [2][1] | [2][2] | [2][3] + * | [2][4] | [2][5] | + * +---------------+--------------+--------------+----------------+----------------+--+--+ + */ + +struct txpwr_ofst2x_conf { + s8 enable; + s8 pwrofst2x_tbl_2g4[3][3]; + s8 pwrofst2x_tbl_5g[3][6]; +}; + +struct xtal_cap_conf { + u8 enable; + u8 xtal_cap; + u8 xtal_cap_fine; +}; + +struct mm_set_txpwr_ofst_req { + union { + struct txpwr_ofst_conf txpwr_ofst; + struct txpwr_ofst2x_conf txpwr_ofst2x; + }; +}; + +struct mm_set_stack_start_req { + u8 is_stack_start; + u8 efuse_valid; + u8 set_vendor_info; + u8 fwtrace_redir; +}; + +struct mm_set_stack_start_cfm { + u8 is_5g_support; + u8 vendor_info; +}; + +/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ +/// message. +struct mm_set_p2p_oppps_req { + /// VIF Index + u8 vif_index; + /// CTWindow + u8 ctwindow; +}; + +/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message. +struct mm_set_p2p_noa_cfm { + /// Request status + u8 status; +}; + +/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM +/// message. +struct mm_set_p2p_oppps_cfm { + /// Request status + u8 status; +}; + +/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message. +struct mm_p2p_noa_upd_ind { + /// VIF Index + u8 vif_index; + /// NOA Instance Number + u8 noa_inst_nb; + /// NoA Type + u8 noa_type; + /// Count + u8 count; + /// Duration (in us) + u32 duration_us; + /// Interval (in us) + u32 interval_us; + /// Start Time + u32 start_time; +}; + +/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message +struct mm_cfg_rssi_req { + /// Index of the VIF + u8 vif_index; + /// RSSI threshold + s8 rssi_thold; + /// RSSI hysteresis + u8 rssi_hyst; +}; + +/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message +struct mm_rssi_status_ind { + /// Index of the VIF + u8 vif_index; + /// Status of the RSSI + bool rssi_status; + /// Current RSSI + s8 rssi; +}; + +/// Structure containing the parameters of the @ref MM_PKTLOSS_IND message +struct mm_pktloss_ind { + /// Index of the VIF + u8 vif_index; + /// Address of the STA for which there is a packet loss + struct mac_addr mac_addr; + /// Number of packets lost + u32 num_packets; +}; + +/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message +struct mm_csa_finish_ind { + /// Index of the VIF + u8 vif_index; + /// Status of the operation + u8 status; + /// New channel ctx index + u8 chan_idx; +}; + +/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message +struct mm_csa_traffic_ind { + /// Index of the VIF + u8 vif_index; + /// Is tx traffic enable or disable + bool enable; +}; + +/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ +/// message. Size allocated for the structure depends of the number of group +struct mm_mu_group_update_req { + /// Station index + u8 sta_idx; + /// Number of groups the STA belongs to + u8 group_cnt; + /// Group information + struct { + /// Group Id + u8 group_id; + /// User position + u8 user_pos; + } groups[]; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For Scan messages +/////////////////////////////////////////////////////////////////////////////// +enum scan_msg_tag { + /// Scanning start Request. + SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN), + /// Scanning start Confirmation. + SCAN_START_CFM, + /// End of scanning indication. + SCAN_DONE_IND, + /// Cancel scan request + SCAN_CANCEL_REQ, + /// Cancel scan confirmation + SCAN_CANCEL_CFM, + + /// MAX number of messages + SCAN_MAX, +}; + +/// Maximum number of SSIDs in a scan request +#define SCAN_SSID_MAX 3 + +/// Maximum number of channels in a scan request +#define SCAN_CHANNEL_MAX (MAC_DOMAINCHANNEL_24G_MAX + MAC_DOMAINCHANNEL_5G_MAX) + +/// Maximum length of the ProbeReq IEs (SoftMAC mode) +#define SCAN_MAX_IE_LEN 300 + +/// Maximum number of PHY bands supported +#define SCAN_BAND_MAX 2 + +/// Structure containing the parameters of the @ref SCAN_START_REQ message +struct scan_start_req { + /// List of channel to be scanned + struct mac_chan_def chan[SCAN_CHANNEL_MAX]; + /// List of SSIDs to be scanned + struct mac_ssid ssid[SCAN_SSID_MAX]; + /// BSSID to be scanned + struct mac_addr bssid; + /// Pointer (in host memory) to the additional IEs that need to be added to + /// the ProbeReq (following the SSID element) + u32 add_ies; + /// Length of the additional IEs + u16 add_ie_len; + /// Index of the VIF that is scanning + u8 vif_idx; + /// Number of channels to scan + u8 chan_cnt; + /// Number of SSIDs to scan for + u8 ssid_cnt; + /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band. + bool no_cck; + /// Scan duration, in us + u32 duration; +}; + +/// Structure containing the parameters of the @ref SCAN_START_CFM message +struct scan_start_cfm { + /// Status of the request + u8 status; +}; + +/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message +struct scan_cancel_req { +}; + +/// Structure containing the parameters of the @ref SCAN_START_CFM message +struct scan_cancel_cfm { + /// Status of the request + u8 status; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For Scanu messages +/////////////////////////////////////////////////////////////////////////////// +/// Messages that are logically related to the task. +enum { + /// Scan request from host. + SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU), + /// Scanning start Confirmation. + SCANU_START_CFM, + /// Join request + SCANU_JOIN_REQ, + /// Join confirmation. + SCANU_JOIN_CFM, + /// Scan result indication. + SCANU_RESULT_IND, + /// Fast scan request from any other module. + SCANU_FAST_REQ, + /// Confirmation of fast scan request. + SCANU_FAST_CFM, + + SCANU_VENDOR_IE_REQ, + SCANU_VENDOR_IE_CFM, + SCANU_START_CFM_ADDTIONAL, + SCANU_CANCEL_REQ, + SCANU_CANCEL_CFM, + + /// MAX number of messages + SCANU_MAX, +}; + +/// Maximum length of the additional ProbeReq IEs (FullMAC mode) +#define SCANU_MAX_IE_LEN 200 + +/// Structure containing the parameters of the @ref SCANU_START_REQ message +struct scanu_start_req { + /// List of channel to be scanned + struct mac_chan_def chan[SCAN_CHANNEL_MAX]; + /// List of SSIDs to be scanned + struct mac_ssid ssid[SCAN_SSID_MAX]; + /// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in + /// particular) + struct mac_addr bssid; + /// Address (in host memory) of the additional IEs that need to be added to + /// the ProbeReq (following the SSID element) + u32 add_ies; + /// Length of the additional IEs + u16 add_ie_len; + /// Index of the VIF that is scanning + u8 vif_idx; + /// Number of channels to scan + u8 chan_cnt; + /// Number of SSIDs to scan for + u8 ssid_cnt; + /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band. + bool no_cck; + /// Scan duration, in us + u32 duration; +}; + +struct scanu_vendor_ie_req { + u16 add_ie_len; + u8 vif_idx; + u8 ie[256]; +}; + +/// Structure containing the parameters of the @ref SCANU_START_CFM message +struct scanu_start_cfm { + /// Index of the VIF that was scanning + u8 vif_idx; + /// Status of the request + u8 status; + /// Number of scan results available + u8 result_cnt; +}; + +/// Parameters of the @SCANU_RESULT_IND message +struct scanu_result_ind { + /// Length of the frame + u16 length; + /// Frame control field of the frame. + u16 framectrl; + /// Center frequency on which we received the packet + u16 center_freq; + /// PHY band + u8 band; + /// Index of the station that sent the frame. 0xFF if unknown. + u8 sta_idx; + /// Index of the VIF that received the frame. 0xFF if unknown. + u8 inst_nbr; + /// RSSI of the received frame. + s8 rssi; + /// Frame payload. + u32 payload[]; +}; + +struct rxu_arp_announcement_ind { + /// index of the vif + u8 vif_index; + u8 offset; + /// length of the frame + u16 length; + u32 rx_status; + /// frame payload + u8 payload[]; +}; + +/// Structure containing the parameters of the message. +struct scanu_fast_req { + /// The SSID to scan in the channel. + struct mac_ssid ssid; + /// BSSID. + struct mac_addr bssid; + /// Probe delay. + u16 probe_delay; + /// Minimum channel time. + u16 minch_time; + /// Maximum channel time. + u16 maxch_time; + /// The channel number to scan. + u16 ch_nbr; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For ME messages +/////////////////////////////////////////////////////////////////////////////// +/// Messages that are logically related to the task. +enum { + /// Configuration request from host. + ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME), + /// Configuration confirmation. + ME_CONFIG_CFM, + /// Configuration request from host. + ME_CHAN_CONFIG_REQ, + /// Configuration confirmation. + ME_CHAN_CONFIG_CFM, + /// Set control port state for a station. + ME_SET_CONTROL_PORT_REQ, + /// Control port setting confirmation. + ME_SET_CONTROL_PORT_CFM, + /// TKIP MIC failure indication. + ME_TKIP_MIC_FAILURE_IND, + /// Add a station to the FW (AP mode) + ME_STA_ADD_REQ, + /// Confirmation of the STA addition + ME_STA_ADD_CFM, + /// Delete a station from the FW (AP mode) + ME_STA_DEL_REQ, + /// Confirmation of the STA deletion + ME_STA_DEL_CFM, + /// Indication of a TX RA/TID queue credit update + ME_TX_CREDITS_UPDATE_IND, + /// Request indicating to the FW that there is traffic buffered on host + ME_TRAFFIC_IND_REQ, + /// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed + ME_TRAFFIC_IND_CFM, + /// Request of RC statistics to a station + ME_RC_STATS_REQ, + /// RC statistics confirmation + ME_RC_STATS_CFM, + /// RC fixed rate request + ME_RC_SET_RATE_REQ, + /// Configure monitor interface + ME_CONFIG_MONITOR_REQ, + /// Configure monitor interface response + ME_CONFIG_MONITOR_CFM, + /// Setting power Save mode request from host + ME_SET_PS_MODE_REQ, + /// Set power Save mode confirmation + ME_SET_PS_MODE_CFM, + /// Setting Low Power level request from host + ME_SET_LP_LEVEL_REQ, + /// Set Low Power level confirmation + ME_SET_LP_LEVEL_CFM, + /// MAX number of messages + ME_MAX, +}; + +/// Structure containing the parameters of the @ref ME_START_REQ message +struct me_config_req { + /// HT Capabilities + struct mac_htcapability ht_cap; + /// VHT Capabilities + struct mac_vhtcapability vht_cap; + /// HE capabilities + struct mac_hecapability he_cap; + /// Lifetime of packets sent under a BlockAck agreement (expressed in TUs) + u16 tx_lft; + /// Maximum supported BW + u8 phy_bw_max; + /// Boolean indicating if HT is supported or not + bool ht_supp; + /// Boolean indicating if VHT is supported or not + bool vht_supp; + /// Boolean indicating if HE is supported or not + bool he_supp; + /// Boolean indicating if HE OFDMA UL is enabled or not + bool he_ul_on; + /// Boolean indicating if PS mode shall be enabled or not + bool ps_on; + /// Boolean indicating if Antenna Diversity shall be enabled or not + bool ant_div_on; + /// Boolean indicating if Dynamic PS mode shall be used or not + bool dpsm; +}; + +/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message +struct me_chan_config_req { + /// List of 2.4GHz supported channels + struct mac_chan_def chan2G4[MAC_DOMAINCHANNEL_24G_MAX]; + /// List of 5GHz supported channels + struct mac_chan_def chan5G[MAC_DOMAINCHANNEL_5G_MAX]; + /// Number of 2.4GHz channels in the list + u8 chan2G4_cnt; + /// Number of 5GHz channels in the list + u8 chan5G_cnt; +}; + +/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ +/// message +struct me_set_control_port_req { + /// Index of the station for which the control port is opened + u8 sta_idx; + /// Control port state + bool control_port_open; +}; + +/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND +/// message +struct me_tkip_mic_failure_ind { + /// Address of the sending STA + struct mac_addr addr; + /// TSC value + u64 tsc; + /// Boolean indicating if the packet was a group or unicast one (true if + /// group) + bool ga; + /// Key Id + u8 keyid; + /// VIF index + u8 vif_idx; +}; + +/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message +struct me_sta_add_req { + /// MAC address of the station to be added + struct mac_addr mac_addr; + /// Supported legacy rates + struct mac_rateset rate_set; + /// HT Capabilities + struct mac_htcapability ht_cap; + /// VHT Capabilities + struct mac_vhtcapability vht_cap; + /// HE capabilities + struct mac_hecapability he_cap; + /// Flags giving additional information about the station (@ref + /// mac_sta_flags) + u32 flags; + /// Association ID of the station + __le16 aid; + /// Bit field indicating which queues have U-APSD enabled + u8 uapsd_queues; + /// Maximum size, in frames, of a APSD service period + u8 max_sp_len; + /// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is + /// set in the flags) + u8 opmode; + /// Index of the VIF the station is attached to + u8 vif_idx; + /// Whether the station is TDLS station + bool tdls_sta; + /// Indicate if the station is TDLS link initiator station + bool tdls_sta_initiator; + /// Indicate if the TDLS Channel Switch is allowed + bool tdls_chsw_allowed; +}; + +/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message +struct me_sta_add_cfm { + /// Station index + u8 sta_idx; + /// Status of the station addition + u8 status; + /// PM state of the station + u8 pm_state; +}; + +/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message. +struct me_sta_del_req { + /// Index of the station to be deleted + u8 sta_idx; + /// Whether the station is TDLS station + bool tdls_sta; +}; + +/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND +/// message. +struct me_tx_credits_update_ind { + /// Index of the station for which the credits are updated + u8 sta_idx; + /// TID for which the credits are updated + u8 tid; + /// Offset to be applied on the credit count + s8 credits; +}; + +/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message. +struct me_traffic_ind_req { + /// Index of the station for which UAPSD traffic is available on host + u8 sta_idx; + /// Flag indicating the availability of UAPSD packets for the given STA + u8 tx_avail; + /// Indicate if traffic is on uapsd-enabled queues + bool uapsd; +}; + +struct mm_apm_staloss_ind { + u8 sta_idx; + u8 vif_idx; + u8 mac_addr[6]; +}; + +enum vendor_hwconfig_tag { + ACS_TXOP_REQ = 0, + CHANNEL_ACCESS_REQ, + MAC_TIMESCALE_REQ, + CCA_THRESHOLD_REQ, + BWMODE_REQ, + CHIP_TEMP_GET_REQ, + AP_PS_LEVEL_SET_REQ, + CUSTOMIZED_FREQ_REQ, + WAKEUP_INFO_REQ, + KEEPALIVE_PKT_REQ, +}; + +enum { + BWMODE20M = 0, + BWMODE10M, + BWMODE5M, +}; + +struct mm_set_acs_txop_req { + u32 hwconfig_id; + u16 txop_bk; + u16 txop_be; + u16 txop_vi; + u16 txop_vo; +}; + +struct mm_set_channel_access_req { + u32 hwconfig_id; + u32 edca[4]; + u8 vif_idx; + u8 retry_cnt; + u8 rts_en; + u8 long_nav_en; + u8 cfe_en; + u8 rc_retry_cnt[3]; + s8 ccademod_th; +}; + +struct mm_set_mac_timescale_req { + u32 hwconfig_id; + u8 sifs_a_time; + u8 sifs_b_time; + u8 slot_time; + u8 rx_startdelay_ofdm; + u8 rx_startdelay_long; + u8 rx_startdelay_short; +}; + +struct mm_set_cca_threshold_req { + u32 hwconfig_id; + u8 auto_cca_en; + s8 cca20p_rise_th; + s8 cca20s_rise_th; + s8 cca20p_fall_th; + s8 cca20s_fall_th; +}; + +struct mm_set_bwmode_req { + u32 hwconfig_id; + u8 bwmode; +}; + +struct mm_get_chip_temp_req { + u32 hwconfig_id; +}; + +struct mm_get_chip_temp_cfm { + /// Temp degree val + s8 degree; +}; + +struct mm_set_ap_ps_level_req { + u32 hwconfig_id; + u8 ap_ps_level; +}; + +struct mm_set_vendor_hwconfig_cfm { + u32 hwconfig_id; + union { + struct mm_get_chip_temp_cfm chip_temp_cfm; + }; +}; + +struct mm_set_customized_freq_req { + u32 hwconfig_id; + u16 raw_freq[4]; + u16 map_freq[4]; +}; + +struct mm_set_keepalive_req { + u32 hwconfig_id; + u16 code; + u16 length; + u32 intv; + u8 payload[]; +}; + +struct mm_set_txop_req { + u16 txop_bk; + u16 txop_be; + u16 txop_vi; + u16 txop_vo; + u8 long_nav_en; + u8 cfe_en; +}; + +struct mm_set_wakeup_info_req { + u32 hwconfig_id; + u16 code; + u16 offset; + u16 length; + u16 length_win; + u8 mask_and_pattern[]; +}; + +struct mm_get_fw_version_cfm { + u8 fw_version_len; + u8 fw_version[63]; +}; + +struct mm_get_wifi_disable_cfm { + u8 wifi_disable; +}; + +enum vendor_swconfig_tag { + BCN_CFG_REQ = 0, + TEMP_COMP_SET_REQ, + TEMP_COMP_GET_REQ, + EXT_FLAGS_SET_REQ, + EXT_FLAGS_GET_REQ, + EXT_FLAGS_MASK_SET_REQ, +}; + +struct mm_set_bcn_cfg_req { + /// Ignore or not bcn tim bcmc bit + bool tim_bcmc_ignored_enable; +}; + +struct mm_set_bcn_cfg_cfm { + /// Request status + bool tim_bcmc_ignored_status; +}; + +struct mm_set_temp_comp_req { + /// Enable or not temp comp + u8 enable; + u8 reserved[3]; + u32 tmr_period_ms; +}; + +struct mm_set_temp_comp_cfm { + /// Request status + u8 status; +}; + +struct mm_get_temp_comp_cfm { + /// Request status + u8 status; + /// Temp degree val + s8 degree; +}; + +struct mm_set_ext_flags_req { + u32 user_flags; +}; + +struct mm_set_ext_flags_cfm { + u32 user_flags; +}; + +struct mm_get_ext_flags_cfm { + u32 user_flags; +}; + +struct mm_mask_set_ext_flags_req { + u32 user_flags_mask; + u32 user_flags_val; +}; + +struct mm_mask_set_ext_flags_cfm { + u32 user_flags; +}; + +struct mm_set_vendor_swconfig_req { + u32 swconfig_id; + union { + struct mm_set_bcn_cfg_req bcn_cfg_req; + struct mm_set_temp_comp_req temp_comp_set_req; + struct mm_set_ext_flags_req ext_flags_set_req; + struct mm_mask_set_ext_flags_req ext_flags_mask_set_req; + }; +}; + +struct mm_set_vendor_swconfig_cfm { + u32 swconfig_id; + union { + struct mm_set_bcn_cfg_cfm bcn_cfg_cfm; + struct mm_set_temp_comp_cfm temp_comp_set_cfm; + struct mm_get_temp_comp_cfm temp_comp_get_cfm; + struct mm_set_ext_flags_cfm ext_flags_set_cfm; + struct mm_get_ext_flags_cfm ext_flags_get_cfm; + struct mm_mask_set_ext_flags_cfm ext_flags_mask_set_cfm; + }; +}; + +/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message. +struct me_rc_stats_req { + /// Index of the station for which the RC statistics are requested + u8 sta_idx; +}; + +/// Structure containing the rate control statistics +struct rc_rate_stats { + /// Number of attempts (per sampling interval) + u16 attempts; + /// Number of success (per sampling interval) + u16 success; + /// Estimated probability of success (EWMA) + u16 probability; + /// Rate configuration of the sample + u16 rate_config; + union { + struct { + /// Number of times the sample has been skipped (per sampling + /// interval) + u8 sample_skipped; + /// Whether the old probability is available + bool old_prob_available; + /// Whether the rate can be used in the retry chain + bool rate_allowed; + }; + struct { + /// RU size and UL length received in the latest HE trigger frame + u16 ru_and_length; + }; + }; +}; + +/// Number of RC samples +#define RC_MAX_N_SAMPLE 10 +/// Index of the HE statistics element in the table +#define RC_HE_STATS_IDX RC_MAX_N_SAMPLE + +/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message. +struct me_rc_stats_cfm { + /// Index of the station for which the RC statistics are provided + u8 sta_idx; + /// Number of samples used in the RC algorithm + u16 no_samples; + /// Number of MPDUs transmitted (per sampling interval) + u16 ampdu_len; + /// Number of AMPDUs transmitted (per sampling interval) + u16 ampdu_packets; + /// Average number of MPDUs in each AMPDU frame (EWMA) + u32 avg_ampdu_len; + // Current step 0 of the retry chain + u8 sw_retry_step; + /// Trial transmission period + u8 sample_wait; + /// Retry chain steps + u16 retry_step_idx[4]; + /// RC statistics - Max number of RC samples, plus one for the HE TB + /// statistics + struct rc_rate_stats rate_stats[RC_MAX_N_SAMPLE + 1]; + /// Throughput - Max number of RC samples, plus one for the HE TB statistics + u32 tp[RC_MAX_N_SAMPLE + 1]; +}; + +/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message. +struct me_rc_set_rate_req { + /// Index of the station for which the fixed rate is set + u8 sta_idx; + /// Rate configuration to be set + u16 fixed_rate_cfg; +}; + +/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_REQ +/// message. +struct me_config_monitor_req { + /// Channel to configure + struct mac_chan_op chan; + /// Is channel data valid + bool chan_set; + /// Enable report of unsupported HT frames + bool uf; + /// Enable auto-reply as the mac_addr matches + bool auto_reply; +}; + +/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_CFM +/// message. +struct me_config_monitor_cfm { + /// Channel context index + u8 chan_index; + /// Channel parameters + struct mac_chan_op chan; +}; + +/// Structure containing the parameters of the @ref ME_SET_PS_MODE_REQ message. +struct me_set_ps_mode_req { + /// Power Save is activated or deactivated + u8 ps_state; +}; + +/// Structure containing the parameters of the @ref ME_SET_LP_LEVEL_REQ message. +struct me_set_lp_level_req { + /// Low Power level + u8 lp_level; + u8 disable_filter; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For SM messages +/////////////////////////////////////////////////////////////////////////////// +/// Message API of the SM task +enum sm_msg_tag { + /// Request to connect to an AP + SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM), + /// Confirmation of connection + SM_CONNECT_CFM, + /// Indicates that the SM associated to the AP + SM_CONNECT_IND, + /// Request to disconnect + SM_DISCONNECT_REQ, + /// Confirmation of disconnection + SM_DISCONNECT_CFM, + /// Indicates that the SM disassociated the AP + SM_DISCONNECT_IND, + /// Request to start external authentication + SM_EXTERNAL_AUTH_REQUIRED_IND, + /// Response to external authentication request + SM_EXTERNAL_AUTH_REQUIRED_RSP, + /// Request to update assoc elements after FT over the air authentication + SM_FT_AUTH_IND, + /// Response to FT authentication with updated assoc elements + SM_FT_AUTH_RSP, + + SM_RSP_TIMEOUT_IND, + + SM_COEX_TS_TIMEOUT_IND, + + SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM, + /// MAX number of messages + SM_MAX, +}; + +/// Structure containing the parameters of @ref SM_CONNECT_REQ message. +struct sm_connect_req { + /// SSID to connect to + struct mac_ssid ssid; + /// BSSID to connect to (if not specified, set this field to WILDCARD BSSID) + struct mac_addr bssid; + /// Channel on which we have to connect (if not specified, set -1 in the + /// chan.freq field) + struct mac_chan_def chan; + /// Connection flags (see @ref mac_connection_flags) + u32 flags; + /// Control port Ethertype (in network endianness) + __be16 ctrl_port_ethertype; + /// Length of the association request IEs + u16 ie_len; + /// Listen interval to be used for this connection + u16 listen_interval; + /// Flag indicating if the we have to wait for the BC/MC traffic after + /// beacon or not + bool dont_wait_bcmc; + /// Authentication type + u8 auth_type; + /// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK) + u8 uapsd_queues; + /// VIF index + u8 vif_idx; + /// Buffer containing the additional information elements to be put in the + /// association request + u32 ie_buf[64]; +}; + +/// Structure containing the parameters of the @ref SM_CONNECT_CFM message. +struct sm_connect_cfm { + /// Status. If 0, it means that the connection procedure will be performed + /// and that a subsequent @ref SM_CONNECT_IND message will be forwarded once + /// the procedure is completed + u8 status; +}; + +#define SM_ASSOC_IE_LEN 800 +/// Structure containing the parameters of the @ref SM_CONNECT_IND message. +struct sm_connect_ind { + /// Status code of the connection procedure + u16 status_code; + /// BSSID + struct mac_addr bssid; + /// Flag indicating if the indication refers to an internal roaming or from + /// a host request + bool roamed; + /// Index of the VIF for which the association process is complete + u8 vif_idx; + /// Index of the STA entry allocated for the AP + u8 ap_idx; + /// Index of the LMAC channel context the connection is attached to + u8 ch_idx; + /// Flag indicating if the AP is supporting QoS + bool qos; + /// ACM bits set in the AP WMM parameter element + u8 acm; + /// Length of the AssocReq IEs + u16 assoc_req_ie_len; + /// Length of the AssocRsp IEs + u16 assoc_rsp_ie_len; + /// IE buffer + u32 assoc_ie_buf[SM_ASSOC_IE_LEN / 4]; + + u16 aid; + u8 band; + u16 center_freq; + u8 width; + u32 center_freq1; + u32 center_freq2; + + /// EDCA parameters + u32 ac_param[AC_MAX]; +}; + +/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message. +struct sm_disconnect_req { + /// Reason of the deauthentication. + u16 reason_code; + /// Index of the VIF. + u8 vif_idx; +}; + +/// Structure containing the parameters of SM_ASSOCIATION_IND the message +struct sm_association_ind { + // MAC ADDR of the STA + struct mac_addr me_mac_addr; +}; + +/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message. +struct sm_disconnect_ind { + /// Reason of the disconnection. + u16 reason_code; + /// Index of the VIF. + u8 vif_idx; + /// FT over DS is ongoing + bool ft_over_ds; + u8 reassoc; +}; + +/// Structure containing the parameters of the @ref +/// SM_EXTERNAL_AUTH_REQUIRED_IND +struct sm_external_auth_required_ind { + /// Index of the VIF. + u8 vif_idx; + /// SSID to authenticate to + struct mac_ssid ssid; + /// BSSID to authenticate to + struct mac_addr bssid; + /// AKM suite of the respective authentication + u32 akm; +}; + +/// Structure containing the parameters of the @ref +/// SM_EXTERNAL_AUTH_REQUIRED_RSP +struct sm_external_auth_required_rsp { + /// Index of the VIF. + u8 vif_idx; + /// Authentication status + u16 status; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For APM messages +/////////////////////////////////////////////////////////////////////////////// +/// Message API of the APM task +enum apm_msg_tag { + /// Request to start the AP. + APM_START_REQ = LMAC_FIRST_MSG(TASK_APM), + /// Confirmation of the AP start. + APM_START_CFM, + /// Request to stop the AP. + APM_STOP_REQ, + /// Confirmation of the AP stop. + APM_STOP_CFM, + /// Request to start CAC + APM_START_CAC_REQ, + /// Confirmation of the CAC start + APM_START_CAC_CFM, + /// Request to stop CAC + APM_STOP_CAC_REQ, + /// Confirmation of the CAC stop + APM_STOP_CAC_CFM, + + APM_SET_BEACON_IE_REQ, + APM_SET_BEACON_IE_CFM, + + /// MAX number of messages + APM_MAX, +}; + +/// Structure containing the parameters of the @ref APM_START_REQ message. +struct apm_start_req { + /// Basic rate set + struct mac_rateset basic_rates; + /// Control channel on which we have to enable the AP + struct mac_chan_def chan; + /// Center frequency of the first segment + u32 center_freq1; + /// Center frequency of the second segment (only in 80+80 configuration) + u32 center_freq2; + /// Width of channel + u8 ch_width; + /// Address, in host memory, to the beacon template + u32 bcn_addr; + /// Length of the beacon template + u16 bcn_len; + /// Offset of the TIM IE in the beacon + u16 tim_oft; + /// Beacon interval + u16 bcn_int; + /// Flags (@ref mac_connection_flags) + u32 flags; + /// Control port Ethertype + __be16 ctrl_port_ethertype; + /// Length of the TIM IE + u8 tim_len; + /// Index of the VIF for which the AP is started + u8 vif_idx; +}; + +struct apm_set_bcn_ie_req { + u8 vif_idx; + u16 bcn_ie_len; + u8 bcn_ie[512]; +}; + +/// Structure containing the parameters of the @ref APM_START_CFM message. +struct apm_start_cfm { + /// Status of the AP starting procedure + u8 status; + /// Index of the VIF for which the AP is started + u8 vif_idx; + /// Index of the channel context attached to the VIF + u8 ch_idx; + /// Index of the STA used for BC/MC traffic + u8 bcmc_idx; +}; + +/// Structure containing the parameters of the @ref APM_STOP_REQ message. +struct apm_stop_req { + /// Index of the VIF for which the AP has to be stopped + u8 vif_idx; +}; + +/// Structure containing the parameters of the @ref APM_START_CAC_REQ message. +struct apm_start_cac_req { + /// Control channel on which we have to start the CAC + struct mac_chan_def chan; + /// Center frequency of the first segment + u32 center_freq1; + /// Center frequency of the second segment (only in 80+80 configuration) + u32 center_freq2; + /// Width of channel + u8 ch_width; + /// Index of the VIF for which the CAC is started + u8 vif_idx; +}; + +/// Structure containing the parameters of the @ref APM_START_CAC_CFM message. +struct apm_start_cac_cfm { + /// Status of the CAC starting procedure + u8 status; + /// Index of the channel context attached to the VIF for CAC + u8 ch_idx; +}; + +/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message. +struct apm_stop_cac_req { + /// Index of the VIF for which the CAC has to be stopped + u8 vif_idx; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For MESH messages +/////////////////////////////////////////////////////////////////////////////// + +/// Maximum length of the Mesh ID +#define MESH_MESHID_MAX_LEN (32) + +/// Message API of the MESH task +enum mesh_msg_tag { + /// Request to start the MP + MESH_START_REQ = LMAC_FIRST_MSG(TASK_MESH), + /// Confirmation of the MP start. + MESH_START_CFM, + + /// Request to stop the MP. + MESH_STOP_REQ, + /// Confirmation of the MP stop. + MESH_STOP_CFM, + + // Request to update the MP + MESH_UPDATE_REQ, + /// Confirmation of the MP update + MESH_UPDATE_CFM, + + /// Request information about a given link + MESH_PEER_INFO_REQ, + /// Response to the MESH_PEER_INFO_REQ message + MESH_PEER_INFO_CFM, + + /// Request automatic establishment of a path with a given mesh STA + MESH_PATH_CREATE_REQ, + /// Confirmation to the MESH_PATH_CREATE_REQ message + MESH_PATH_CREATE_CFM, + + /// Request a path update (delete path, modify next hop mesh STA) + MESH_PATH_UPDATE_REQ, + /// Confirmation to the MESH_PATH_UPDATE_REQ message + MESH_PATH_UPDATE_CFM, + + /// Indication from Host that the indicated Mesh Interface is a proxy for an + /// external STA + MESH_PROXY_ADD_REQ, + + /// Indicate that a connection has been established or lost + MESH_PEER_UPDATE_IND, + /// Notification that a connection has been established or lost (when MPM + /// handled by userspace) + MESH_PEER_UPDATE_NTF = MESH_PEER_UPDATE_IND, + + /// Indicate that a path is now active or inactive + MESH_PATH_UPDATE_IND, + /// Indicate that proxy information have been updated + MESH_PROXY_UPDATE_IND, + + /// MAX number of messages + MESH_MAX, +}; + +/// Structure containing the parameters of the @ref MESH_START_REQ message. +struct mesh_start_req { + /// Basic rate set + struct mac_rateset basic_rates; + /// Control channel on which we have to enable the AP + struct mac_chan_def chan; + /// Center frequency of the first segment + u32 center_freq1; + /// Center frequency of the second segment (only in 80+80 configuration) + u32 center_freq2; + /// Width of channel + u8 ch_width; + /// DTIM Period + u8 dtim_period; + /// Beacon Interval + u16 bcn_int; + /// Index of the VIF for which the MP is started + u8 vif_index; + /// Length of the Mesh ID + u8 mesh_id_len; + /// Mesh ID + u8 mesh_id[MESH_MESHID_MAX_LEN]; + /// Address of the IEs to download + u32 ie_addr; + /// Length of the provided IEs + u8 ie_len; + /// Indicate if Mesh Peering Management (MPM) protocol is handled in + /// userspace + bool user_mpm; + /// Indicate if Mesh Point is using authentication + bool is_auth; + /// Indicate which authentication method is used + u8 auth_id; +}; + +/// Structure containing the parameters of the @ref MESH_START_CFM message. +struct mesh_start_cfm { + /// Status of the MP starting procedure + u8 status; + /// Index of the VIF for which the MP is started + u8 vif_idx; + /// Index of the channel context attached to the VIF + u8 ch_idx; + /// Index of the STA used for BC/MC traffic + u8 bcmc_idx; +}; + +/// Structure containing the parameters of the @ref MESH_STOP_REQ message. +struct mesh_stop_req { + /// Index of the VIF for which the MP has to be stopped + u8 vif_idx; +}; + +/// Structure containing the parameters of the @ref MESH_STOP_CFM message. +struct mesh_stop_cfm { + /// Index of the VIF for which the MP has to be stopped + u8 vif_idx; + /// Status + u8 status; +}; + +/// Bit fields for mesh_update_req message's flags value +enum mesh_update_flags_bit { + /// Root Mode + MESH_UPDATE_FLAGS_ROOT_MODE_BIT = 0, + /// Gate Mode + MESH_UPDATE_FLAGS_GATE_MODE_BIT, + /// Mesh Forwarding + MESH_UPDATE_FLAGS_MESH_FWD_BIT, + /// Local Power Save Mode + MESH_UPDATE_FLAGS_LOCAL_PSM_BIT, +}; + +/// Structure containing the parameters of the @ref MESH_UPDATE_REQ message. +struct mesh_update_req { + /// Flags, indicate fields which have been updated + u8 flags; + /// VIF Index + u8 vif_idx; + /// Root Mode + u8 root_mode; + /// Gate Announcement + bool gate_announ; + /// Mesh Forwarding + bool mesh_forward; + /// Local PS Mode + u8 local_ps_mode; +}; + +/// Structure containing the parameters of the @ref MESH_UPDATE_CFM message. +struct mesh_update_cfm { + /// Status + u8 status; +}; + +/// Structure containing the parameters of the @ref MESH_PEER_INFO_REQ message. +struct mesh_peer_info_req { + /// Index of the station allocated for the peer + u8 sta_idx; +}; + +/// Structure containing the parameters of the @ref MESH_PEER_INFO_CFM message. +struct mesh_peer_info_cfm { + /// Response status + u8 status; + /// Index of the station allocated for the peer + u8 sta_idx; + /// Local Link ID + u16 local_link_id; + /// Peer Link ID + u16 peer_link_id; + /// Local PS Mode + u8 local_ps_mode; + /// Peer PS Mode + u8 peer_ps_mode; + /// Non-peer PS Mode + u8 non_peer_ps_mode; + /// Link State + u8 link_state; +}; + +/// Structure containing the parameters of the @ref MESH_PATH_CREATE_REQ +/// message. +struct mesh_path_create_req { + /// Index of the interface on which path has to be created + u8 vif_idx; + /// Indicate if originator MAC Address is provided + bool has_orig_addr; + /// Path Target MAC Address + struct mac_addr tgt_mac_addr; + /// Originator MAC Address + struct mac_addr orig_mac_addr; +}; + +/// Structure containing the parameters of the @ref MESH_PATH_CREATE_CFM +/// message. +struct mesh_path_create_cfm { + /// Confirmation status + u8 status; + /// VIF Index + u8 vif_idx; +}; + +/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_REQ +/// message. +struct mesh_path_update_req { + /// Indicate if path must be deleted + bool delete; + /// Index of the interface on which path has to be created + u8 vif_idx; + /// Path Target MAC Address + struct mac_addr tgt_mac_addr; + /// Next Hop MAC Address + struct mac_addr nhop_mac_addr; +}; + +/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_CFM +/// message. +struct mesh_path_update_cfm { + /// Confirmation status + u8 status; + /// VIF Index + u8 vif_idx; +}; + +/// Structure containing the parameters of the @ref MESH_PROXY_ADD_REQ message. +struct mesh_proxy_add_req { + /// VIF Index + u8 vif_idx; + /// MAC Address of the External STA + struct mac_addr ext_sta_addr; +}; + +/// Structure containing the parameters of the @ref MESH_PROXY_UPDATE_IND +struct mesh_proxy_update_ind { + /// Indicate if proxy information has been added or deleted + bool delete; + /// Indicate if we are a proxy for the external STA + bool local; + /// VIF Index + u8 vif_idx; + /// MAC Address of the External STA + struct mac_addr ext_sta_addr; + /// MAC Address of the proxy (only valid if local is false) + struct mac_addr proxy_mac_addr; +}; + +/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_IND +/// message. +struct mesh_peer_update_ind { + /// Indicate if connection has been established or lost + bool estab; + /// VIF Index + u8 vif_idx; + /// STA Index + u8 sta_idx; + /// Peer MAC Address + struct mac_addr peer_addr; +}; + +/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_NTF +/// message. +struct mesh_peer_update_ntf { + /// VIF Index + u8 vif_idx; + /// STA Index + u8 sta_idx; + /// Mesh Link State + u8 state; +}; + +/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_IND +/// message. +struct mesh_path_update_ind { + /// Indicate if path is deleted or not + bool delete; + /// Indicate if path is towards an external STA (not part of MBSS) + bool ext_sta; + /// VIF Index + u8 vif_idx; + /// Path Index + u8 path_idx; + /// Target MAC Address + struct mac_addr tgt_mac_addr; + /// External STA MAC Address (only if ext_sta is true) + struct mac_addr ext_sta_mac_addr; + /// Next Hop STA Index + u8 nhop_sta_idx; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For Debug messages +/////////////////////////////////////////////////////////////////////////////// + +/// Messages related to Debug Task +enum dbg_msg_tag { + /// Memory read request + DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG), + /// Memory read confirm + DBG_MEM_READ_CFM, + /// Memory write request + DBG_MEM_WRITE_REQ, + /// Memory write confirm + DBG_MEM_WRITE_CFM, + /// Module filter request + DBG_SET_MOD_FILTER_REQ, + /// Module filter confirm + DBG_SET_MOD_FILTER_CFM, + /// Severity filter request + DBG_SET_SEV_FILTER_REQ, + /// Severity filter confirm + DBG_SET_SEV_FILTER_CFM, + /// LMAC/MAC HW fatal error indication + DBG_ERROR_IND, + /// Request to get system statistics + DBG_GET_SYS_STAT_REQ, + /// COnfirmation of system statistics + DBG_GET_SYS_STAT_CFM, + /// Memory block write request + DBG_MEM_BLOCK_WRITE_REQ, + /// Memory block write confirm + DBG_MEM_BLOCK_WRITE_CFM, + /// Start app request + DBG_START_APP_REQ, + /// Start app confirm + DBG_START_APP_CFM, + /// Start npc request + DBG_START_NPC_REQ, + /// Start npc confirm + DBG_START_NPC_CFM, + /// Memory mask write request + DBG_MEM_MASK_WRITE_REQ, + /// Memory mask write confirm + DBG_MEM_MASK_WRITE_CFM, + + DBG_RFTEST_CMD_REQ, + DBG_RFTEST_CMD_CFM, + DBG_BINDING_REQ, + DBG_BINDING_CFM, + DBG_BINDING_IND, + + DBG_CUSTOM_MSG_REQ, + DBG_CUSTOM_MSG_CFM, + DBG_CUSTOM_MSG_IND, + + DBG_GPIO_WRITE_REQ, + DBG_GPIO_WRITE_CFM, + DBG_GPIO_READ_REQ, + DBG_GPIO_READ_CFM, + DBG_GPIO_INIT_REQ, + DBG_GPIO_INIT_CFM, + + /// EF usrdata read request + DBG_EF_USRDATA_READ_REQ, + /// EF usrdata read confirm + DBG_EF_USRDATA_READ_CFM, + /// Memory block read request + DBG_MEM_BLOCK_READ_REQ, + /// Memory block read confirm + DBG_MEM_BLOCK_READ_CFM, + + DBG_PWM_INIT_REQ, + DBG_PWM_INIT_CFM, + DBG_PWM_DEINIT_REQ, + DBG_PWM_DEINIT_CFM, + + /// Max number of Debug messages + DBG_MAX, +}; + +/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message. +struct dbg_mem_read_req { + u32 memaddr; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message. +struct dbg_mem_read_cfm { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message. +struct dbg_mem_write_req { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message. +struct dbg_mem_write_cfm { + u32 memaddr; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_REQ +/// message. +struct dbg_mem_mask_write_req { + u32 memaddr; + u32 memmask; + u32 memdata; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_CFM +/// message. +struct dbg_mem_mask_write_cfm { + u32 memaddr; + u32 memdata; +}; + +struct dbg_rftest_cmd_req { + u32 cmd; + u32 argc; + u8 argv[30]; +}; + +struct dbg_rftest_cmd_cfm { + u32 rftest_result[18]; +}; + +struct dbg_gpio_write_req { + u8 gpio_idx; + u8 gpio_val; +}; + +struct dbg_gpio_read_req { + u8 gpio_idx; +}; + +struct dbg_gpio_read_cfm { + u8 gpio_idx; + u8 gpio_val; +}; + +struct dbg_gpio_init_req { + u8 gpio_idx; + u8 gpio_dir; // 1 output, 0 input; + u8 gpio_val; // for output, 1 high, 0 low; +}; + +/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ +/// message. +struct dbg_set_mod_filter_req { + /// Bit field indicating for each module if the traces are enabled or not + u32 mod_filter; +}; + +/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ +/// message. +struct dbg_set_sev_filter_req { + /// Bit field indicating the severity threshold for the traces + u32 sev_filter; +}; + +/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM +/// message. +struct dbg_get_sys_stat_cfm { + /// Time spent in CPU sleep since last reset of the system statistics + u32 cpu_sleep_time; + /// Time spent in DOZE since last reset of the system statistics + u32 doze_time; + /// Total time spent since last reset of the system statistics + u32 stats_time; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_REQ +/// message. +struct dbg_mem_block_write_req { + u32 memaddr; + u32 memsize; + u32 memdata[1024 / sizeof(u32)]; +}; + +/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_CFM +/// message. +struct dbg_mem_block_write_cfm { + u32 wstatus; +}; + +/// Structure containing the parameters of the @ref DBG_START_APP_REQ message. +struct dbg_start_app_req { + u32 bootaddr; + u32 boottype; +}; + +/// Structure containing the parameters of the @ref DBG_START_APP_CFM message. +struct dbg_start_app_cfm { + u32 bootstatus; +}; + +enum { + HOST_START_APP_AUTO = 1, + HOST_START_APP_CUSTOM, + HOST_START_APP_FNCALL = 4, + HOST_START_APP_DUMMY = 5, +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////// For TDLS messages +/////////////////////////////////////////////////////////////////////////////// + +/// List of messages related to the task. +enum tdls_msg_tag { + /// TDLS channel Switch Request. + TDLS_CHAN_SWITCH_REQ = LMAC_FIRST_MSG(TASK_TDLS), + /// TDLS channel switch confirmation. + TDLS_CHAN_SWITCH_CFM, + /// TDLS channel switch indication. + TDLS_CHAN_SWITCH_IND, + /// TDLS channel switch to base channel indication. + TDLS_CHAN_SWITCH_BASE_IND, + /// TDLS cancel channel switch request. + TDLS_CANCEL_CHAN_SWITCH_REQ, + /// TDLS cancel channel switch confirmation. + TDLS_CANCEL_CHAN_SWITCH_CFM, + /// TDLS peer power save indication. + TDLS_PEER_PS_IND, + /// TDLS peer traffic indication request. + TDLS_PEER_TRAFFIC_IND_REQ, + /// TDLS peer traffic indication confirmation. + TDLS_PEER_TRAFFIC_IND_CFM, + + /// MAX number of messages + TDLS_MAX +}; + +/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_REQ message +struct tdls_chan_switch_req { + /// Index of the VIF + u8 vif_index; + /// STA Index + u8 sta_idx; + /// MAC address of the TDLS station + struct mac_addr peer_mac_addr; + bool initiator; + /// Band (2.4GHz or 5GHz) + u8 band; + /// Channel type: 20,40,80,160 or 80+80 MHz + u8 type; + /// Frequency for Primary 20MHz channel (in MHz) + u16 prim20_freq; + /// Frequency for Center of the contiguous channel or center of Primary + /// 80+80 + u16 center1_freq; + /// Frequency for Center of the non-contiguous secondary 80+80 + u16 center2_freq; + /// TX power (in dBm) + s8 tx_power; + /// Operating class + u8 op_class; +}; + +/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_REQ +/// message +struct tdls_cancel_chan_switch_req { + /// Index of the VIF + u8 vif_index; + /// STA Index + u8 sta_idx; + /// MAC address of the TDLS station + struct mac_addr peer_mac_addr; +}; + +/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_CFM message +struct tdls_chan_switch_cfm { + /// Status of the operation + u8 status; +}; + +/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_CFM +/// message +struct tdls_cancel_chan_switch_cfm { + /// Status of the operation + u8 status; +}; + +/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_IND message +struct tdls_chan_switch_ind { + /// VIF Index + u8 vif_index; + /// Channel Context Index + u8 chan_ctxt_index; + /// Status of the operation + u8 status; +}; + +/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_BASE_IND +/// message +struct tdls_chan_switch_base_ind { + /// VIF Index + u8 vif_index; + /// Channel Context index + u8 chan_ctxt_index; +}; + +/// Structure containing the parameters of the @ref TDLS_PEER_PS_IND message +struct tdls_peer_ps_ind { + /// VIF Index + u8 vif_index; + /// STA Index + u8 sta_idx; + /// MAC ADDR of the TDLS STA + struct mac_addr peer_mac_addr; + /// Flag to indicate if the TDLS peer is going to sleep + bool ps_on; +}; + +/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_REQ +/// message +struct tdls_peer_traffic_ind_req { + /// VIF Index + u8 vif_index; + /// STA Index + u8 sta_idx; + // MAC ADDR of the TDLS STA + struct mac_addr peer_mac_addr; + /// Dialog token + u8 dialog_token; + /// TID of the latest MPDU transmitted over the TDLS direct link to the TDLS + /// STA + u8 last_tid; + /// Sequence number of the latest MPDU transmitted over the TDLS direct link + /// to the TDLS STA + u16 last_sn; +}; + +/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_CFM +/// message +struct tdls_peer_traffic_ind_cfm { + /// Status of the operation + u8 status; +}; + +#endif // LMAC_MSG_H_ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h b/drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h new file mode 100644 index 0000000000000..76f251607a7b9 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/lmac_types.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file co_types.h + * + * @brief Provides the kernel fixed-width types used by LMAC interfaces. + * + **************************************************************************************** + */ + +#ifndef _LMAC_INT_H_ +#define _LMAC_INT_H_ + +#include +#include +#include + +/// @} CO_INT +#endif // _LMAC_INT_H_ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/reg_access.h b/drivers/net/wireless/aic/aic8800_fdrv/reg_access.h new file mode 100644 index 0000000000000..2654ab8523436 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/reg_access.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Definitions and macros for MAC HW and platform register accesses + * + ****************************************************************************** + */ + +#ifndef REG_ACCESS_H_ +#define REG_ACCESS_H_ + +/***************************************************************************** + * Addresses within RWNX_ADDR_SYSTEM + *****************************************************************************/ +/* Shard RAM */ +#define SHARED_RAM_START_ADDR 0x00000000 + +/* IPC registers */ +#define IPC_REG_BASE_ADDR 0x00800000 + +/* System Controller Registers */ +#define SYSCTRL_SIGNATURE_ADDR 0x00900000 +// old diag register name +#define SYSCTRL_DIAG_CONF_ADDR 0x00900068 +#define SYSCTRL_PHYDIAG_CONF_ADDR 0x00900074 +#define SYSCTRL_RIUDIAG_CONF_ADDR 0x00900078 +// new diag register name +#define SYSCTRL_DIAG_CONF0 0x00900064 +#define SYSCTRL_DIAG_CONF1 0x00900068 +#define SYSCTRL_DIAG_CONF2 0x00900074 +#define SYSCTRL_DIAG_CONF3 0x00900078 +#define SYSCTRL_MISC_CNTL_ADDR 0x009000E0 +#define BOOTROM_ENABLE BIT(4) +#define FPGA_B_RESET BIT(1) +#define SOFT_RESET BIT(0) + +/* MAC platform */ +#define NXMAC_VERSION_1_ADDR 0x00B00004 +#define NXMAC_MU_MIMO_TX_BIT BIT(19) +#define NXMAC_BFMER_BIT BIT(18) +#define NXMAC_BFMEE_BIT BIT(17) +#define NXMAC_MAC_80211MH_FORMAT_BIT BIT(16) +#define NXMAC_COEX_BIT BIT(14) +#define NXMAC_WAPI_BIT BIT(13) +#define NXMAC_TPC_BIT BIT(12) +#define NXMAC_VHT_BIT BIT(11) +#define NXMAC_HT_BIT BIT(10) +#define NXMAC_RCE_BIT BIT(8) +#define NXMAC_CCMP_BIT BIT(7) +#define NXMAC_TKIP_BIT BIT(6) +#define NXMAC_WEP_BIT BIT(5) +#define NXMAC_SECURITY_BIT BIT(4) +#define NXMAC_SME_BIT BIT(3) +#define NXMAC_HCCA_BIT BIT(2) +#define NXMAC_EDCA_BIT BIT(1) +#define NXMAC_QOS_BIT BIT(0) + +#define NXMAC_RX_CNTRL_ADDR 0x00B00060 +#define NXMAC_EN_DUPLICATE_DETECTION_BIT BIT(31) +#define NXMAC_ACCEPT_UNKNOWN_BIT BIT(30) +#define NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT BIT(29) +#define NXMAC_ACCEPT_QO_S_NULL_BIT BIT(28) +#define NXMAC_ACCEPT_QCFWO_DATA_BIT BIT(27) +#define NXMAC_ACCEPT_Q_DATA_BIT BIT(26) +#define NXMAC_ACCEPT_CFWO_DATA_BIT BIT(25) +#define NXMAC_ACCEPT_DATA_BIT BIT(24) +#define NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT BIT(23) +#define NXMAC_ACCEPT_CF_END_BIT BIT(22) +#define NXMAC_ACCEPT_ACK_BIT BIT(21) +#define NXMAC_ACCEPT_CTS_BIT BIT(20) +#define NXMAC_ACCEPT_RTS_BIT BIT(19) +#define NXMAC_ACCEPT_PS_POLL_BIT BIT(18) +#define NXMAC_ACCEPT_BA_BIT BIT(17) +#define NXMAC_ACCEPT_BAR_BIT BIT(16) +#define NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT BIT(15) +#define NXMAC_ACCEPT_BFMEE_FRAMES_BIT BIT(14) +#define NXMAC_ACCEPT_ALL_BEACON_BIT BIT(13) +#define NXMAC_ACCEPT_NOT_EXPECTED_BA_BIT BIT(12) +#define NXMAC_ACCEPT_DECRYPT_ERROR_FRAMES_BIT BIT(11) +#define NXMAC_ACCEPT_BEACON_BIT BIT(10) +#define NXMAC_ACCEPT_PROBE_RESP_BIT BIT(9) +#define NXMAC_ACCEPT_PROBE_REQ_BIT BIT(8) +#define NXMAC_ACCEPT_MY_UNICAST_BIT BIT(7) +#define NXMAC_ACCEPT_UNICAST_BIT BIT(6) +#define NXMAC_ACCEPT_ERROR_FRAMES_BIT BIT(5) +#define NXMAC_ACCEPT_OTHER_BSSID_BIT BIT(4) +#define NXMAC_ACCEPT_BROADCAST_BIT BIT(3) +#define NXMAC_ACCEPT_MULTICAST_BIT BIT(2) +#define NXMAC_DONT_DECRYPT_BIT BIT(1) +#define NXMAC_EXC_UNENCRYPTED_BIT BIT(0) + +#define NXMAC_DEBUG_PORT_SEL_ADDR 0x00B00510 +#define NXMAC_SW_SET_PROFILING_ADDR 0x00B08564 +#define NXMAC_SW_CLEAR_PROFILING_ADDR 0x00B08568 + +/* Modem Status */ +#define MDM_HDMCONFIG_ADDR 0x00C00000 + +/* Clock gating configuration */ +#define MDM_MEMCLKCTRL0_ADDR 0x00C00848 +#define MDM_CLKGATEFCTRL0_ADDR 0x00C00874 +#define CRM_CLKGATEFCTRL0_ADDR 0x00940010 + +/* AGC (trident) */ +#define AGC_RWNXAGCCNTL_ADDR 0x00C02060 + +/* LDPC RAM*/ +#define PHY_LDPC_RAM_ADDR 0x00C09000 + +/* FCU (elma )*/ +#define FCU_RWNXFCAGCCNTL_ADDR 0x00C09034 + +/* AGC RAM */ +#define PHY_AGC_UCODE_ADDR 0x00C0A000 + +/* RIU */ +#define RIU_RWNXVERSION_ADDR 0x00C0B000 +#define RIU_RWNXDYNAMICCONFIG_ADDR 0x00C0B008 +#define RIU_AGCMEMBISTSTAT_ADDR 0x00C0B238 +#define RIU_AGCMEMSIGNATURESTAT_ADDR 0x00C0B23C +#define RIU_RWNXAGCCNTL_ADDR 0x00C0B390 + +/* FCU RAM */ +#define PHY_FCU_UCODE_ADDR 0x00C0E000 + +/* RF ITF */ +#define FPGAB_MPIF_SEL_ADDR 0x00C10030 +#define RF_V6_DIAGPORT_CONF1_ADDR 0x00C10010 +#define RF_v6_PHYDIAG_CONF1_ADDR 0x00C10018 + +#define RF_V7_DIAGPORT_CONF1_ADDR 0x00F10010 +#define RF_v7_PHYDIAG_CONF1_ADDR 0x00F10018 + +/***************************************************************************** + * Macros for generated register files + *****************************************************************************/ +/* Macros for IPC registers access (used in reg_ipc_app.h) */ +#define REG_IPC_APP_RD(env, INDEX) \ + (*(u32 *)((u8 *)(env) + IPC_REG_BASE_ADDR + 4 * (INDEX))) + +#define REG_IPC_APP_WR(env, INDEX, value) \ + (*(u32 *)((u8 *)(env) + IPC_REG_BASE_ADDR + 4 * (INDEX)) = value) + +#endif /* REG_ACCESS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c new file mode 100644 index 0000000000000..2bb0c58dc984e --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to + * LMAC FW + * + ****************************************************************************** + */ + +#include "rwnx_cmds.h" +#include "aicwf_txrxif.h" +#include "rwnx_defs.h" +#include "rwnx_events.h" +#include "rwnx_strs.h" +#include +#ifdef AICWF_SDIO_SUPPORT +#include "aicwf_sdio.h" +#else +#include "aicwf_usb.h" +#endif + +static void cmd_dump(const struct rwnx_cmd *cmd) +{ + if (!cmd) { + AICWFDBG(LOGDEBUG, KERN_ERR "AICWF %s cmd is null\n", __func__); + return; + } + AICWFDBG(LOGDEBUG, KERN_CRIT + "tkn[%d] flags:%04x result:%3d cmd:%4d-%-24s - reqcfm(%4d-%-s)\n", + cmd->tkn, cmd->flags, cmd->result, cmd->id, RWNX_ID2STR(cmd->id), + cmd->reqid, + cmd->reqid != (lmac_msg_id_t)-1 ? RWNX_ID2STR(cmd->reqid) : "none"); +} + +static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) +{ + // RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace + lockdep_assert_held(&cmd_mgr->lock); + + list_del(&cmd->list); + cmd_mgr->queue_sz--; + + cmd->flags |= RWNX_CMD_FLAG_DONE; + if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) { + rwnx_cmd_free(cmd); // kfree(cmd); + } else { + if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) { + cmd->result = 0; + complete(&cmd->complete); + } + } +} + +int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd *cmd) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_msg_send(cmd->id); +#endif + spin_lock_bh(&cmd_mgr->lock); + + if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) { + AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd queue crashed\n"); + cmd->result = -EPIPE; + spin_unlock_bh(&cmd_mgr->lock); + return -EPIPE; + } + + if (!list_empty(&cmd_mgr->cmds)) { + if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) { + AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF Too many cmds (%d) already queued\n", + cmd_mgr->max_queue_sz); + cmd->result = -ENOMEM; + spin_unlock_bh(&cmd_mgr->lock); + return -ENOMEM; + } + } + + cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH; + + if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM) + cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM; + + cmd->tkn = cmd_mgr->next_tkn++; + cmd->result = -EINTR; + + if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) + init_completion(&cmd->complete); + + list_add_tail(&cmd->list, &cmd_mgr->cmds); + cmd_mgr->queue_sz++; + spin_unlock_bh(&cmd_mgr->lock); + + WAKE_CMD_WORK(cmd_mgr); + return 0; +} + +static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) +{ +#ifdef AICWF_SDIO_SUPPORT + int ret; + struct aic_sdio_dev *sdiodev = + container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr); +#endif + bool defer_push = false; + + // RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_msg_send(cmd->id); +#endif + spin_lock_bh(&cmd_mgr->lock); + + if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) { + AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd queue crashed\n"); + cmd->result = -EPIPE; + spin_unlock_bh(&cmd_mgr->lock); + return -EPIPE; + } + + if (!list_empty(&cmd_mgr->cmds)) { + struct rwnx_cmd *last; + + if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) { + AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF Too many cmds (%d) already queued\n", + cmd_mgr->max_queue_sz); + cmd->result = -ENOMEM; + spin_unlock_bh(&cmd_mgr->lock); + return -ENOMEM; + } + last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list); + if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH | + RWNX_CMD_FLAG_WAIT_CFM)) { + cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH; + defer_push = true; + } + } + + if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM) + cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM; + + cmd->tkn = cmd_mgr->next_tkn++; + cmd->result = -EINTR; + + if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) + init_completion(&cmd->complete); + + list_add_tail(&cmd->list, &cmd_mgr->cmds); + cmd_mgr->queue_sz++; + +#ifdef AICWF_ARP_OFFLOAD + if (cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ || + cmd->a2e_msg->id == MM_SET_ARPOFFLOAD_REQ) { +#else + if (cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ) { +#endif + defer_push = true; + cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH; + } + + spin_unlock_bh(&cmd_mgr->lock); + if (!defer_push) { +#ifdef AICWF_SDIO_SUPPORT + aicwf_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg, + sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); +#else + aicwf_set_cmd_tx((void *)(usbdev), cmd->a2e_msg, + sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); +#endif + kfree(cmd->a2e_msg); + } else { + if (cmd_mgr->queue_sz <= 1) + WAKE_CMD_WORK(cmd_mgr); + return 0; + } + + if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) { + unsigned long tout = + msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz); + if (!wait_for_completion_timeout(&cmd->complete, tout)) { + AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd timed-out\n"); +#ifdef AICWF_SDIO_SUPPORT + ret = aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, 2); + if (ret < 0) { + sdio_err("reg:%d write failed!\n", + sdiodev->sdio_reg.wakeup_reg); + } +#endif + + cmd_dump(cmd); + spin_lock_bh(&cmd_mgr->lock); + cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED; + if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) { + cmd->result = -ETIMEDOUT; + cmd_complete(cmd_mgr, cmd); + } + spin_unlock_bh(&cmd_mgr->lock); + } else { + rwnx_cmd_free(cmd); // kfree(cmd); + if (!list_empty(&cmd_mgr->cmds)) + WAKE_CMD_WORK(cmd_mgr); + } + } else { + cmd->result = 0; + } + + return 0; +} + +static int cmd_mgr_llind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd) +{ + struct rwnx_cmd *cur, *acked = NULL; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + spin_lock_bh(&cmd_mgr->lock); + list_for_each_entry(cur, &cmd_mgr->cmds, list) { + if (!acked) { + if (cur->tkn == cmd->tkn) { + if (WARN_ON_ONCE(cur != cmd)) + cmd_dump(cmd); + acked = cur; + continue; + } + } + if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) + break; + } + if (!acked) { + AICWFDBG(LOGDEBUG, KERN_CRIT "Error: acked cmd not found\n"); + } else { + cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; + if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) + cmd_complete(cmd_mgr, cmd); + } + spin_unlock(&cmd_mgr->lock); + + return 0; +} + +void cmd_mgr_task_process(struct work_struct *work) +{ + struct rwnx_cmd_mgr *cmd_mgr = + container_of(work, struct rwnx_cmd_mgr, cmd_work); + struct rwnx_cmd *cur, *next = NULL; + unsigned long tout; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + while (1) { + next = NULL; + spin_lock_bh(&cmd_mgr->lock); + + list_for_each_entry(cur, &cmd_mgr->cmds, list) { + if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) + next = cur; + break; + } + spin_unlock_bh(&cmd_mgr->lock); + + if (!next) + break; + + if (next) { +#ifdef AICWF_SDIO_SUPPORT + struct aic_sdio_dev *sdiodev = + container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr); +#endif + next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH; + +#ifdef AICWF_SDIO_SUPPORT + aicwf_set_cmd_tx((void *)(sdiodev), next->a2e_msg, + sizeof(struct lmac_msg) + + next->a2e_msg->param_len); +#else + aicwf_set_cmd_tx((void *)(usbdev), next->a2e_msg, + sizeof(struct lmac_msg) + + next->a2e_msg->param_len); +#endif + kfree(next->a2e_msg); + + tout = + msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz); + if (!wait_for_completion_timeout(&next->complete, tout)) { + AICWFDBG(LOGDEBUG, KERN_CRIT "AICWF cmd timed-out\n"); +#ifdef AICWF_SDIO_SUPPORT + if (aicwf_sdio_writeb(sdiodev, sdiodev->sdio_reg.wakeup_reg, + 2) < 0) { + sdio_err("reg:%d write failed!\n", + sdiodev->sdio_reg.wakeup_reg); + } +#endif + cmd_dump(next); + spin_lock_bh(&cmd_mgr->lock); + cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED; + if (!(next->flags & RWNX_CMD_FLAG_DONE)) { + next->result = -ETIMEDOUT; + cmd_complete(cmd_mgr, next); + } + spin_unlock_bh(&cmd_mgr->lock); + } else { + rwnx_cmd_free(next); // kfree(next); + } + } + } +} + +static int cmd_mgr_run_callback(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, + struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb) +{ + int res; + + if (!cb) + return 0; + + res = cb(rwnx_hw, cmd, msg); + + return res; +} + +static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb) +{ +#ifdef AICWF_SDIO_SUPPORT + struct aic_sdio_dev *sdiodev = + container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr); + struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw; +#endif + struct rwnx_cmd *cmd, *pos; + bool found = false; + + // RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_msg_recv(msg->id); +#endif + // AICWFDBG(LOGDEBUG, "cmd->id=%x\n", msg->id); //debug trace + spin_lock_bh(&cmd_mgr->lock); + list_for_each_entry_safe(cmd, pos, &cmd_mgr->cmds, list) { + if (cmd->reqid == msg->id && (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) { + if (!cmd_mgr_run_callback(rwnx_hw, cmd, msg, cb)) { + found = true; + cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM; + if (WARN(msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX, + "Unexpect E2A msg len %d > %d\n", msg->param_len, + RWNX_CMD_E2AMSG_LEN_MAX)) + msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX; + + if (cmd->e2a_msg && msg->param_len) + memcpy(cmd->e2a_msg, &msg->param, msg->param_len); + + if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) + cmd_complete(cmd_mgr, cmd); + + break; + } + } + } + spin_unlock_bh(&cmd_mgr->lock); + + if (!found) + cmd_mgr_run_callback(rwnx_hw, NULL, msg, cb); + + return 0; +} + +static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr) +{ + struct rwnx_cmd *cur; + + spin_lock_bh(&cmd_mgr->lock); + RWNX_DBG("q_sz/max: %2d / %2d - next tkn: %d\n", cmd_mgr->queue_sz, + cmd_mgr->max_queue_sz, cmd_mgr->next_tkn); + list_for_each_entry(cur, &cmd_mgr->cmds, list) { + cmd_dump(cur); + } + spin_unlock_bh(&cmd_mgr->lock); +} + +static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr) +{ + struct rwnx_cmd *cur, *nxt; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + spin_lock_bh(&cmd_mgr->lock); + list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) { + list_del(&cur->list); + cmd_mgr->queue_sz--; + if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK)) + complete(&cur->complete); + } + spin_unlock_bh(&cmd_mgr->lock); +} + +void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); + + INIT_LIST_HEAD(&cmd_mgr->cmds); + cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED; + spin_lock_init(&cmd_mgr->lock); + cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED; + cmd_mgr->queue = &cmd_mgr_queue; + cmd_mgr->print = &cmd_mgr_print; + cmd_mgr->drain = &cmd_mgr_drain; + cmd_mgr->llind = &cmd_mgr_llind; + cmd_mgr->msgind = &cmd_mgr_msgind; + + INIT_WORK(&cmd_mgr->cmd_work, cmd_mgr_task_process); + cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq"); + if (!cmd_mgr->cmd_wq) { + txrx_err("insufficient memory to create cmd workqueue.\n"); + return; + } +} + +void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr) +{ + cmd_mgr->print(cmd_mgr); + cmd_mgr->drain(cmd_mgr); + cmd_mgr->print(cmd_mgr); + flush_workqueue(cmd_mgr->cmd_wq); + destroy_workqueue(cmd_mgr->cmd_wq); + memset(cmd_mgr, 0, sizeof(*cmd_mgr)); +} + +void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len) +{ + u8 *buffer = NULL; + u16 index = 0; +#ifdef AICWF_SDIO_SUPPORT + struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev; + struct aicwf_bus *bus = sdiodev->bus_if; +#else + struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev; + struct aicwf_bus *bus = NULL; + + if (!usbdev->state) { + AICWFDBG(LOGDEBUG, "down msg\n"); + return; + } + bus = usbdev->bus_if; +#endif + buffer = bus->cmd_buf; + + memset(buffer, 0, CMD_BUF_MAX); + buffer[0] = (len + 4) & 0x00ff; + buffer[1] = ((len + 4) >> 8) & 0x0f; + buffer[2] = 0x11; + + buffer[3] = aic_chip_cmd_hdr_checksum(sdiodev->rwnx_hw, &buffer[0], 3); + + index += 4; + // there is a dummy word + index += 4; + + // make sure little endian + put_u16(&buffer[index], msg->id); + index += 2; + put_u16(&buffer[index], msg->dest_id); + index += 2; + put_u16(&buffer[index], msg->src_id); + index += 2; + put_u16(&buffer[index], msg->param_len); + index += 2; + memcpy(&buffer[index], (u8 *)msg->param, msg->param_len); + + aicwf_bus_txmsg(bus, buffer, len + 8); +} diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h new file mode 100644 index 0000000000000..adb4bc49c188a --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_cmds.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RWNX_CMDS_H_ +#define _RWNX_CMDS_H_ + +#include "lmac_msg.h" +#include "ipc_shared.h" +#include +#include +#include + +#define RWNX_80211_CMD_TIMEOUT_MS 6000 + +#define RWNX_CMD_FLAG_NONBLOCK BIT(0) +#define RWNX_CMD_FLAG_REQ_CFM BIT(1) +#define RWNX_CMD_FLAG_WAIT_PUSH BIT(2) +#define RWNX_CMD_FLAG_WAIT_ACK BIT(3) +#define RWNX_CMD_FLAG_WAIT_CFM BIT(4) +#define RWNX_CMD_FLAG_DONE BIT(5) +/* ATM IPC design makes it possible to get the CFM before the ACK, + * otherwise this could have simply been a state enum + */ +#define RWNX_CMD_WAIT_COMPLETE(flags) \ + (!((flags) & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_CFM))) + +#define RWNX_CMD_MAX_QUEUED 16 + +#define rwnx_cmd_e2amsg ipc_e2a_msg +#define rwnx_cmd_a2emsg lmac_msg +#define RWNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + (m)->param_len) +#define RWNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4) + +struct rwnx_hw; +struct rwnx_cmd; +typedef int (*msg_cb_fct)(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd, + struct rwnx_cmd_e2amsg *msg); + +static inline void put_u16(u8 *buf, u16 data) +{ + buf[0] = (u8)(data & 0x00ff); + buf[1] = (u8)((data >> 8) & 0x00ff); +} + +enum rwnx_cmd_mgr_state { + RWNX_CMD_MGR_STATE_DEINIT, + RWNX_CMD_MGR_STATE_INITED, + RWNX_CMD_MGR_STATE_CRASHED, +}; + +struct rwnx_cmd { + struct list_head list; + lmac_msg_id_t id; + lmac_msg_id_t reqid; + struct rwnx_cmd_a2emsg *a2e_msg; + char *e2a_msg; + u32 tkn; + u16 flags; + + struct completion complete; + u32 result; + u8 used; + int array_id; +}; + +struct rwnx_cmd_mgr { + enum rwnx_cmd_mgr_state state; + spinlock_t lock; // + u32 next_tkn; + u32 queue_sz; + u32 max_queue_sz; + + struct list_head cmds; + + int (*queue)(struct rwnx_cmd_mgr *mgr, struct rwnx_cmd *cmd); + int (*llind)(struct rwnx_cmd_mgr *mgr, struct rwnx_cmd *cmd); + int (*msgind)(struct rwnx_cmd_mgr *mgr, struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb); + void (*print)(struct rwnx_cmd_mgr *mgr); + void (*drain)(struct rwnx_cmd_mgr *mgr); + + struct work_struct cmd_work; + struct workqueue_struct *cmd_wq; +}; + +#define WAKE_CMD_WORK(_cmd_mgr) \ + do { \ + struct rwnx_cmd_mgr *__mgr = (_cmd_mgr); \ + queue_work((__mgr)->cmd_wq, &__mgr->cmd_work); \ + } while (0) + +void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr); +void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr); +int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, + struct rwnx_cmd *cmd); +void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len); + +void cmd_mgr_task_process(struct work_struct *work); + +void rwnx_cmd_free(struct rwnx_cmd *cmd); + +#endif /* _RWNX_CMDS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h new file mode 100644 index 0000000000000..9a127b659e2b6 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_compat.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Ensure driver compilation for linux 3.16 to 3.19 + * To avoid too many #if LINUX_VERSION_CODE if the code, when prototype change + * between different kernel version: + * - For external function, define a macro whose name is the function name with + * _compat suffix and prototype (actually the number of parameter) of the + * latest version. Then latest version this macro simply call the function + * and for older kernel version it call the function adapting the api. + * - For internal function (e.g. cfg80211_ops) do the same but the macro name + * doesn't need to have the _compat suffix when the function is not used + * directly by the driver + * + ****************************************************************************** + */ + +#ifndef _RWNX_COMPAT_H_ +#define _RWNX_COMPAT_H_ +#include + +#include + +/* CFG80211 */ + +#ifdef CONFIG_RWNX_MUMIMO_TX +#define CONFIG_USER_MAX 2 +#else +#define CONFIG_USER_MAX 1 +#endif + +#define NX_VIRT_DEV_MAX 4 + +#define NX_REMOTE_STA_MAX_FOR_OLD_IC 10 +#define NX_REMOTE_STA_MAX 32 + +#define NX_MU_GROUP_MAX 62 +#define NX_TXDESC_CNT 64 +#define NX_TX_MAX_RATES 4 +#define NX_CHAN_CTXT_CNT 3 + +#ifdef CONFIG_RWNX_BCMC +#define NX_TXQ_CNT 5 +#else +#define NX_TXQ_CNT 4 +#endif + +#define IEEE80211_MAX_AMPDU_BUF IEEE80211_MAX_AMPDU_BUF_HE +#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB \ + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB +#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB \ + IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB +#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA \ + IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU + +#define CCFS0(vht) ((vht)->center_freq_seg0_idx) +#define CCFS1(vht) ((vht)->center_freq_seg1_idx) + +#define SURVEY_TIME(s) ((s)->time) +#define SURVEY_TIME_BUSY(s) ((s)->time_busy) +#define STA_TDLS_INITIATOR(sta) ((sta)->tdls_initiator) + +#define RX_ENC_HT(s) ((s)->encoding = RX_ENC_HT) +#define RX_ENC_VHT(s) ((s)->encoding = RX_ENC_VHT) +#define RX_ENC_HE(s) ((s)->encoding = RX_ENC_HE) +#define RX_ENC_FLAG_SHORT_GI(s) ((s)->enc_flags |= RX_ENC_FLAG_SHORT_GI) +#define RX_ENC_FLAG_SHORT_PRE(s) ((s)->enc_flags |= RX_ENC_FLAG_SHORTPRE) +#define RX_ENC_FLAG_LDPC(s) ((s)->enc_flags |= RX_ENC_FLAG_LDPC) +#define RX_BW_40MHZ(s) ((s)->bw = RATE_INFO_BW_40) +#define RX_BW_80MHZ(s) ((s)->bw = RATE_INFO_BW_80) +#define RX_BW_160MHZ(s) ((s)->bw = RATE_INFO_BW_160) +#define RX_NSS(s) ((s)->nss) + +#ifndef CONFIG_VENDOR_RWNX_AMSDUS_TX +#endif /* CONFIG_VENDOR_RWNX_AMSDUS_TX */ + +#endif /* _RWNX_COMPAT_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h new file mode 100644 index 0000000000000..eae8d04bee9dd --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_defs.h @@ -0,0 +1,704 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Main driver structure declarations for fullmac driver + * + ****************************************************************************** + */ + +#ifndef _RWNX_DEFS_H_ +#define _RWNX_DEFS_H_ + +#include +#include +#include +#include +#include +#include + +#include "rwnx_cmds.h" +#include "rwnx_mod_params.h" +#include "rwnx_mu_group.h" +#include "rwnx_platform.h" +#include "rwnx_radar.h" +#include "rwnx_rx.h" +#include "rwnx_tx.h" +#include "rwnx_utils.h" +#include "rwnx_compat.h" +#ifdef CONFIG_AIC8800_FILTER_TCP_ACK +#include "aicwf_tcp_ack.h" +#endif + +#ifdef AICWF_SDIO_SUPPORT +#include "aicwf_sdio.h" +#include "sdio_host.h" +#endif + +#include "lmac_mac.h" +#include "aicwf_chip_ops.h" + +#define WPI_HDR_LEN 18 +#define WPI_PN_LEN 16 +#define WPI_PN_OFST 2 +#define WPI_MIC_LEN 16 +#define WPI_KEY_LEN 32 +#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key + +#define LEGACY_PS_ID 0 +#define UAPSD_ID 1 + +#define PS_SP_INTERRUPTED 255 +#define MAC_ADDR_LEN 6 + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG +#define MAX_SEARCH_CNT 10 + +/// Structure containing the parameters of the auto ccode +struct scanu_ccode { + /// ccode auto/manual switch, auto--true, manual--false + bool auto_set; + /// ccode find + bool ccode_find; + /// ccode set + bool ccode_set; + /// ccode find cnt + u8 ccode_cnt; + /// ccode rssi + s8 ccode_rssi; + /// link scan + atomic_t link_scan; + /// record mac addr + struct mac_addr bssid; +}; +#endif + +struct wf_bss_info { + u32 length; + s8 rssi; + u16 chan; + enum nl80211_band band; /* Band */ + u8 width; /* Channel width 0/1/2--20/40/80 */ + u16 center_freq; /* Center frequency */ + u32 center_freq1; /* Center frequency 1 */ + u32 center_freq2; /* Center frequency 2 */ + bool ht; + bool vht; + u8 bssid[ETH_ALEN]; + u8 ssid_len; + unsigned char ssid[33]; +}; + +/** + * struct rwnx_bcn - Information of the beacon in used (AP mode) + * + * @head: head portion of beacon (before TIM IE) + * @tail: tail portion of beacon (after TIM IE) + * @ies: extra IEs (not used ?) + * @head_len: length of head data + * @tail_len: length of tail data + * @ies_len: length of extra IEs data + * @tim_len: length of TIM IE + * @len: Total beacon len (head + tim + tail + extra) + * @dtim: dtim period + */ +struct rwnx_bcn { + u8 *head; + u8 *tail; + u8 *ies; + size_t head_len; + size_t tail_len; + size_t ies_len; + size_t tim_len; + size_t len; + u8 dtim; +}; + +/** + * struct rwnx_key - Key information + * + * @hw_idx: Idx of the key from hardware point of view + */ +struct rwnx_key { + u8 hw_idx; +}; + +/* + * Structure containing information about a Mesh Path + */ +struct rwnx_mesh_path { + struct list_head list; /* For rwnx_vif.mesh_paths */ + u8 path_idx; /* Path Index */ + struct mac_addr tgt_mac_addr; /* Target MAC Address */ + struct rwnx_sta *p_nhop_sta; /* Pointer to the Next Hop STA */ +}; + +struct rwnx_mesh_proxy { + struct list_head list; /* For rwnx_vif.mesh_proxy */ + struct mac_addr ext_sta_addr; /* Address of the External STA */ + struct mac_addr proxy_addr; /* Proxy MAC Address */ + bool local; /* Indicate if interface is a proxy for the device */ +}; + +/** + * struct rwnx_csa - Information for CSA (Channel Switch Announcement) + * + * @vif: Pointer to the vif doing the CSA + * @bcn: Beacon to use after CSA + * @elem: IPC buffer to send the new beacon to the fw + * @chandef: defines the channel to use after the switch + * @count: Current csa counter + * @status: Status of the CSA at fw level + * @ch_idx: Index of the new channel context + * @work: work scheduled at the end of CSA + */ +struct rwnx_csa { + struct rwnx_vif *vif; + struct rwnx_bcn bcn; + struct rwnx_ipc_elem_var elem; + struct cfg80211_chan_def chandef; + int count; + int status; + int ch_idx; + struct work_struct work; +}; + +struct apm_probe_sta { + u8 sta_mac_addr[6]; + u8 vif_idx; + u64 probe_id; + struct work_struct apmprobesta_work; + struct workqueue_struct *apmprobesta_wq; +}; + +/// Possible States of the TDLS link. +enum tdls_status_tag { + /// TDLS link is not active (no TDLS peer connected) + TDLS_LINK_IDLE, + /// TDLS Setup Request transmitted + TDLS_SETUP_REQ_TX, + /// TDLS Setup Response transmitted + TDLS_SETUP_RSP_TX, + /// TDLS link is active (TDLS peer connected) + TDLS_LINK_ACTIVE, + /// TDLS Max Number of states. + TDLS_STATE_MAX +}; + +/* + * Structure used to save information relative to the TDLS peer. + * This is also linked within the rwnx_hw vifs list. + * + */ +struct rwnx_tdls { + bool active; /* Indicate if TDLS link is active */ + bool initiator; /* Indicate if TDLS peer is the TDLS initiator */ + bool chsw_en; /* Indicate if channel switch is enabled */ + u8 last_tid; /* TID of the latest MPDU transmitted over the + * TDLS direct link to the TDLS STA + */ + u16 last_sn; /* Sequence number of the latest MPDU transmitted + * over the TDLS direct link to the TDLS STA + */ + bool ps_on; /* Indicate if the power save is enabled on the + * TDLS STA + */ + bool chsw_allowed; /* Indicate if TDLS channel switch is allowed */ +}; + +/** + * enum rwnx_ap_flags - AP flags + * + * @RWNX_AP_ISOLATE: Isolate clients (i.e. don't bridge packets transmitted by + * one client for another one) + */ +enum rwnx_ap_flags { + RWNX_AP_ISOLATE = BIT(0), +}; + +/* + * Structure used to save information relative to the managed interfaces. + * This is also linked within the rwnx_hw vifs list. + * + */ +struct rwnx_vif { + struct list_head list; + struct rwnx_hw *rwnx_hw; + struct wireless_dev wdev; + struct net_device *ndev; + struct net_device_stats net_stats; + struct rwnx_key key[6]; + unsigned long drv_flags; + atomic_t drv_conn_state; + u8 drv_vif_index; /* Identifier of the VIF in driver */ + u8 vif_index; /* Identifier of the station in FW */ + u8 ch_index; /* Channel context identifier */ + bool up; /* Indicate if associated netdev is up + * (i.e. Interface is created at fw level) + */ + bool use_4addr; /* Should we use 4addresses mode */ + bool is_resending; /* Indicate if a frame is being resent on this interface */ + bool user_mpm; /* In case of Mesh Point VIF, indicate if MPM is handled by + * userspace + */ + bool roc_tdls; /* Indicate if the ROC has been called by a + * TDLS station + */ + u8 tdls_status; /* Status of the TDLS link */ + bool tdls_chsw_prohibited; /* Indicate if TDLS Channel Switch is prohibited */ + bool wep_enabled; /* 1 if WEP is enabled */ + bool wep_auth_err; /* 1 if auth status code is not supported auth alg when + * WEP enabled + */ + enum nl80211_auth_type + last_auth_type; /* Authentication type (algorithm) sent in the last + * connection when WEP enabled + */ + union { + struct { + struct rwnx_sta *ap; /* Pointer to the peer STA entry allocated for + * the AP + */ + struct rwnx_sta *tdls_sta; /* Pointer to the TDLS station */ + bool external_auth; /* Indicate if external authentication is in + * progress + */ + u32 group_cipher_type; + u32 paired_cipher_type; + // connected network info start + char ssid[32]; + int ssid_len; + u8 bssid[ETH_ALEN]; + u32 conn_owner_nlportid; + bool is_roam; + unsigned long link_time; + u16 b_interval; + u8 dtim_peroid; + u16 capability; + enum nl80211_auth_type auth_type; + // connected network info end + } sta; + struct { + u16 flags; /* see rwnx_ap_flags */ + struct list_head sta_list; /* List of STA connected to the AP */ + struct rwnx_bcn bcn; /* beacon */ + u8 bcmc_index; /* Index of the BCMC sta to use */ + struct rwnx_csa *csa; + + struct list_head + mpath_list; /* List of Mesh Paths used on this interface */ + struct list_head proxy_list; /* List of Proxies Information used on + * this interface + */ + bool create_path; /* Indicate if we are waiting for a + * MESH_CREATE_PATH_CFM message + */ + int generation; /* Increased each time the list of Mesh Paths is + * updated + */ + enum nl80211_mesh_power_mode + mesh_pm; /* mesh power save mode currently set in firmware */ + enum nl80211_mesh_power_mode + next_mesh_pm; /* mesh power save mode for next peer */ + } ap; + struct { + struct rwnx_vif *master; /* pointer on master interface */ + struct rwnx_sta *sta_4a; + } ap_vlan; + }; + + u8 key_has_add; + u8 is_p2p_vif; + struct apm_probe_sta sta_probe; + +}; + +#define RWNX_VIF_TYPE(rwnx_vif) ((rwnx_vif)->wdev.iftype) + +/* + * Structure used to store information relative to PS mode. + * + * @active: True when the sta is in PS mode. + * If false, other values should be ignored + * @pkt_ready: Number of packets buffered for the sta in drv's txq + * (1 counter for Legacy PS and 1 for U-APSD) + * @sp_cnt: Number of packets that remain to be pushed in the service period. + * 0 means that no service period is in progress + * (1 counter for Legacy PS and 1 for U-APSD) + */ +struct rwnx_sta_ps { + bool active; + u16 pkt_ready[2]; + u16 sp_cnt[2]; +}; + +/** + * struct rwnx_rx_rate_stats - Store statistics for RX rates + * + * @table: Number of received frames for each rate index + * @size: Number of entries in @table + * @cpt: Total number of received frames + * @rate_cnt: Number of rate indexes with at least one received frame + */ +struct rwnx_rx_rate_stats { + int *table; + int size; + int cpt; + int rate_cnt; +}; + +/** + * struct rwnx_sta_stats - Structure Used to store statistics specific to a STA + * + * @last_rx: Hardware vector of the last received frame + * @rx_rate: Statistics of the received rates + */ +struct rwnx_sta_stats { + struct hw_vect last_rx; + struct rwnx_rx_rate_stats rx_rate; +}; + +/* + * Structure used to save information relative to the managed stations. + */ +struct rwnx_sta { + struct list_head list; + u16 aid; /* association ID */ + u8 sta_idx; /* Identifier of the station */ + u8 vif_idx; /* Identifier of the VIF (fw id) the station + * belongs to + */ + u8 vlan_idx; /* Identifier of the VLAN VIF (fw id) the station + * belongs to (= vif_idx if no vlan in used) + */ + enum nl80211_band band; /* Band */ + enum nl80211_chan_width width; /* Channel width */ + u16 center_freq; /* Center frequency */ + u32 center_freq1; /* Center frequency 1 */ + u32 center_freq2; /* Center frequency 2 */ + u8 ch_idx; /* Identifier of the channel + * context the station belongs to + */ + bool qos; /* Flag indicating if the station + * supports QoS + */ + u8 acm; /* Bitfield indicating which queues + * have AC mandatory + */ + u16 uapsd_tids; /* Bitfield indicating which tids are subject to + * UAPSD + */ + u8 mac_addr[ETH_ALEN]; /* MAC address of the station */ + struct rwnx_key key; + bool valid; /* Flag indicating if the entry is valid */ + struct rwnx_sta_ps ps; /* Information when STA is in PS (AP only) */ +#ifdef CONFIG_RWNX_BFMER + struct rwnx_bfmer_report *bfm_report; /* Beamforming report to be used for + * VHT TX Beamforming + */ +#ifdef CONFIG_RWNX_MUMIMO_TX + struct rwnx_sta_group_info + group_info; /* MU grouping information for the STA */ +#endif /* CONFIG_RWNX_MUMIMO_TX */ +#endif /* CONFIG_RWNX_BFMER */ + + bool ht; /* Flag indicating if the station + * supports HT + */ + bool vht; /* Flag indicating if the station + * supports VHT + */ + u32 ac_param[AC_MAX]; /* EDCA parameters */ + struct rwnx_tdls tdls; /* TDLS station information */ + struct rwnx_sta_stats stats; + enum nl80211_mesh_power_mode + mesh_pm; /* link-specific mesh power save mode */ +}; + +static inline const u8 *rwnx_sta_addr(struct rwnx_sta *rwnx_sta) +{ + return rwnx_sta->mac_addr; +} + +#ifdef CONFIG_RWNX_SPLIT_TX_BUF +struct rwnx_amsdu_stats { + int done; + int failed; +}; +#endif + +struct rwnx_stats { + int cfm_balance[NX_TXQ_CNT]; + unsigned long last_rx, last_tx; /* jiffies */ + int ampdus_tx[IEEE80211_MAX_AMPDU_BUF]; + int ampdus_rx[IEEE80211_MAX_AMPDU_BUF]; + int ampdus_rx_map[4]; + int ampdus_rx_miss; +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + struct rwnx_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX]; +#endif + int amsdus_rx[64]; +}; + +struct rwnx_sec_phy_chan { + u16 prim20_freq; + u16 center_freq1; + u16 center_freq2; + enum nl80211_band band; + u8 type; +}; + +/* Structure that will contains all RoC information received from cfg80211 */ +struct rwnx_roc_elem { + struct wireless_dev *wdev; + struct ieee80211_channel *chan; + unsigned int duration; + u64 cookie; + /* Used to avoid call of CFG80211 callback upon expiration of RoC */ + bool mgmt_roc; + /* Indicate if we have switch on the RoC channel */ + bool on_chan; +}; + +/* Structure containing channel survey information received from MAC */ +struct rwnx_survey_info { + // Filled + u32 filled; + // Amount of time in ms the radio spent on the channel + u32 chan_time_ms; + // Amount of time the primary channel was sensed busy + u32 chan_time_busy_ms; + // Noise in dbm + s8 noise_dbm; +}; + +#define RWNX_CH_NOT_SET 0xFF +#define RWNX_INVALID_VIF 0xFF +#define RWNX_INVALID_STA 0xFF + +/* Structure containing channel context information */ +struct rwnx_chanctx { + struct cfg80211_chan_def chan_def; /* channel description */ + u8 count; /* number of vif using this ctxt */ +}; + +/** + * struct rwnx_phy_info - PHY information + * + * @cnt: Number of PHY interfaces + * @cfg: Configuration sent to the firmware + * @sec_chan: Channel configuration of the second PHY interface, when present + * @limit_bw: Whether to limit the requested channel bandwidth for legacy PHYs + */ +struct rwnx_phy_info { + u8 cnt; + struct phy_cfg_tag cfg; + struct rwnx_sec_phy_chan sec_chan; + bool limit_bw; +}; + +struct defrag_ctrl_info { + struct list_head list; + u8 sta_idx; + u8 tid; + u16 sn; + u8 next_fn; + u16 frm_len; + struct sk_buff *skb; + struct timer_list defrag_timer; + struct rwnx_hw *rwnx_hw; +}; + +struct amsdu_subframe_hdr { + u8 da[6]; + u8 sa[6]; + u16 sublen; +}; + +/* rwnx driver status */ +void rwnx_set_conn_state(atomic_t *drv_conn_state, int state); + +enum rwnx_drv_connect_status { + RWNX_DRV_STATUS_DISCONNECTED = 0, + RWNX_DRV_STATUS_DISCONNECTING, + RWNX_DRV_STATUS_CONNECTING, + RWNX_DRV_STATUS_CONNECTED, + RWNX_DRV_STATUS_ROAMING, +}; + +struct rwnx_hw { + struct rwnx_mod_params *mod_params; + struct device *dev; +#ifdef AICWF_SDIO_SUPPORT + struct aic_sdio_dev *sdiodev; +#endif + struct wiphy *wiphy; + struct list_head vifs; + struct rwnx_vif * + vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */ + struct rwnx_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX]; + struct rwnx_survey_info survey[SCAN_CHANNEL_MAX]; + struct cfg80211_scan_request *scan_request; + /* The normal scan path owns one SDIO activity until its terminal event. */ + atomic_t scan_activity_held; +#ifdef CONFIG_AIC8800_SCHED_SCAN + struct cfg80211_sched_scan_request *sched_scan_req; +#endif + struct rwnx_chanctx chanctx_table[NX_CHAN_CTXT_CNT]; + u8 cur_chanctx; + + u8 monitor_vif; /* FW id of the monitor interface, RWNX_INVALID_VIF if no + * monitor vif at fw level + */ + +#ifdef CONFIG_AIC8800_FILTER_TCP_ACK + /* tcp ack management */ + struct tcp_ack_manage ack_m; +#endif + + /* RoC Management */ + struct rwnx_roc_elem *roc_elem; /* Information provided by cfg80211 in its + * remain on channel request + */ + + struct rwnx_cmd_mgr *cmd_mgr; + + struct rwnx_plat *plat; + + spinlock_t tx_lock; // + spinlock_t cb_lock; // + struct mutex mutex; /* per-device perimeter lock */ + + struct tasklet_struct task; + struct mm_version_cfm + version_cfm; /* Lower layers versions - obtained via MM_VERSION_REQ */ + + u32 tcp_pacing_shift; + + /* IPC */ + struct ipc_host_env_tag *ipc_env; +#ifdef AICWF_SDIO_SUPPORT + struct sdio_host_env_tag sdio_env; +#endif + + struct rwnx_ipc_elem_pool e2amsgs_pool; + struct rwnx_ipc_elem_pool dbgmsgs_pool; + struct rwnx_ipc_elem_pool e2aradars_pool; + struct rwnx_ipc_elem_var pattern_elem; + struct rwnx_ipc_dbgdump_elem dbgdump_elem; + struct rwnx_ipc_elem_pool e2arxdesc_pool; + struct rwnx_ipc_skb_elem *e2aunsuprxvec_elems; + // struct rwnx_ipc_rxbuf_elems rxbuf_elems; + struct rwnx_ipc_elem_var scan_ie; + + struct kmem_cache *sw_txhdr_cache; + + struct rwnx_stats stats; + +#ifdef CONFIG_PREALLOC_TXQ + struct rwnx_txq *txq; +#else + struct rwnx_txq txq[NX_NB_TXQ]; +#endif + + struct rwnx_hwq hwq[NX_TXQ_CNT]; + + u64 avail_idx_map; + u8 vif_started; + bool adding_sta; + struct rwnx_phy_info phy; + + struct rwnx_radar radar; + + /* extended capabilities supported */ + u8 ext_capa[8]; + +#ifdef CONFIG_RWNX_MUMIMO_TX + struct rwnx_mu_info mu; +#endif + u8 is_p2p_alive; + u8 is_p2p_connected; + struct timer_list p2p_alive_timer; + struct rwnx_vif *p2p_dev_vif; + atomic_t p2p_alive_timer_count; + bool band_5g_support; + u8 vendor_info; + bool fwlog_en; + int testmode; + u8 scanning; + char country_abbr[4]; + const struct aic_chip_ops *chip_ops; + + struct list_head defrag_list; + spinlock_t defrag_lock; // + + struct work_struct apm_staloss_work; + struct workqueue_struct *apm_staloss_wq; + u8 apm_vif_idx; + u8 sta_mac_addr[6]; + + struct wakeup_source *ws_rx; + struct wakeup_source *ws_irqrx; + struct wakeup_source *ws_tx; + struct wakeup_source *ws_pwrctrl; + struct wakeup_source *ws_scan; + +#ifdef CONFIG_AIC8800_SCHED_SCAN + bool is_sched_scan; +#endif + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + struct scanu_ccode ccode; +#endif + +#ifdef CONFIG_AIC8800_TEMP_CONTROL + unsigned long started_jiffies; + s8 temp; +#endif +#ifdef CONFIG_AIC8800_POWER_LIMIT + struct work_struct pwlt_wk; +#endif +#ifdef CONFIG_AIC8800_REGION_PW + struct work_struct region_wk; +#endif + char fw_version[32]; + +}; + +u8 *rwnx_build_bcn(struct rwnx_bcn *bcn, struct cfg80211_beacon_data *new); + +void rwnx_chanctx_link(struct rwnx_vif *vif, u8 idx, + struct cfg80211_chan_def *chandef); +void rwnx_chanctx_unlink(struct rwnx_vif *vif); +int rwnx_chanctx_valid(struct rwnx_hw *rwnx_hw, u8 idx); + +extern u8 chip_id; + +static inline bool is_multicast_sta(int sta_idx) +{ + if (g_rwnx_plat->sdiodev->rwnx_hw->chip_ops->is_old_ic) + return (sta_idx >= NX_REMOTE_STA_MAX_FOR_OLD_IC); + else + return (sta_idx >= NX_REMOTE_STA_MAX); +} + +struct rwnx_sta *rwnx_get_sta(struct rwnx_hw *rwnx_hw, const u8 *mac_addr); + +static inline uint8_t master_vif_idx(struct rwnx_vif *vif) +{ +#if IS_ENABLED(CONFIG_AIC8800_STA_ONLY) + return vif->vif_index; +#else + if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) + return vif->ap_vlan.master->vif_index; + else + return vif->vif_index; +#endif +} + +void rwnx_external_auth_enable(struct rwnx_vif *vif); +void rwnx_external_auth_disable(struct rwnx_vif *vif); + +#endif /* _RWNX_DEFS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h new file mode 100644 index 0000000000000..c40751ea8b980 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_events.h @@ -0,0 +1,938 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief Trace events definition + * + ****************************************************************************** + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rwnx + +#if !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _RWNX_EVENTS_H + +#include +#include "rwnx_tx.h" + +#include "rwnx_compat.h" + +/***************************************************************************** + * TRACE function for MGMT TX (FULLMAC) + ****************************************************************************/ +#ifdef CONFIG_RWNX_FULLMAC +#include "linux/ieee80211.h" +#if defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) && \ + !defined(TRACE_HEADER_MULTI_READ) +#include + +/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification, + * section 4.2.8) + */ +/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */ +#define MGMT_ACTION_PUBLIC_CAT (0x04) +/* Offset of OUI Subtype field in P2P Action Frame format */ +#define MGMT_ACTION_OUI_SUBTYPE_OFFSET (6) +/* P2P Public Action Frame Types */ +enum p2p_action_type { + P2P_ACTION_GO_NEG_REQ = 0, /* GO Negociation Request */ + P2P_ACTION_GO_NEG_RSP, /* GO Negociation Response */ + P2P_ACTION_GO_NEG_CFM, /* GO Negociation Confirmation */ + P2P_ACTION_INVIT_REQ, /* P2P Invitation Request */ + P2P_ACTION_INVIT_RSP, /* P2P Invitation Response */ + P2P_ACTION_DEV_DISC_REQ, /* Device Discoverability Request */ + P2P_ACTION_DEV_DISC_RSP, /* Device Discoverability Response */ + P2P_ACTION_PROV_DISC_REQ, /* Provision Discovery Request */ + P2P_ACTION_PROV_DISC_RSP, /* Provision Discovery Response */ +}; + +static inline const char *ftrace_print_mgmt_info(struct trace_seq *p, + u16 frame_control, u8 cat, u8 type, + u8 p2p) +{ + const char *ret = trace_seq_buffer_ptr(p); + + switch (frame_control & IEEE80211_FCTL_STYPE) { + case (IEEE80211_STYPE_ASSOC_REQ): + trace_seq_printf(p, "Association Request"); + break; + case (IEEE80211_STYPE_ASSOC_RESP): + trace_seq_printf(p, "Association Response"); + break; + case (IEEE80211_STYPE_REASSOC_REQ): + trace_seq_printf(p, "Reassociation Request"); + break; + case (IEEE80211_STYPE_REASSOC_RESP): + trace_seq_printf(p, "Reassociation Response"); + break; + case (IEEE80211_STYPE_PROBE_REQ): + trace_seq_printf(p, "Probe Request"); + break; + case (IEEE80211_STYPE_PROBE_RESP): + trace_seq_printf(p, "Probe Response"); + break; + case (IEEE80211_STYPE_BEACON): + trace_seq_printf(p, "Beacon"); + break; + case (IEEE80211_STYPE_ATIM): + trace_seq_printf(p, "ATIM"); + break; + case (IEEE80211_STYPE_DISASSOC): + trace_seq_printf(p, "Disassociation"); + break; + case (IEEE80211_STYPE_AUTH): + trace_seq_printf(p, "Authentication"); + break; + case (IEEE80211_STYPE_DEAUTH): + trace_seq_printf(p, "Deauthentication"); + break; + case (IEEE80211_STYPE_ACTION): + trace_seq_printf(p, "Action"); + if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9) { + switch (p2p) { + case (P2P_ACTION_GO_NEG_REQ): + trace_seq_printf(p, ": GO Negociation Request"); + break; + case (P2P_ACTION_GO_NEG_RSP): + trace_seq_printf(p, ": GO Negociation Response"); + break; + case (P2P_ACTION_GO_NEG_CFM): + trace_seq_printf(p, ": GO Negociation Confirmation"); + break; + case (P2P_ACTION_INVIT_REQ): + trace_seq_printf(p, ": P2P Invitation Request"); + break; + case (P2P_ACTION_INVIT_RSP): + trace_seq_printf(p, ": P2P Invitation Response"); + break; + case (P2P_ACTION_DEV_DISC_REQ): + trace_seq_printf(p, ": Device Discoverability Request"); + break; + case (P2P_ACTION_DEV_DISC_RSP): + trace_seq_printf(p, ": Device Discoverability Response"); + break; + case (P2P_ACTION_PROV_DISC_REQ): + trace_seq_printf(p, ": Provision Discovery Request"); + break; + case (P2P_ACTION_PROV_DISC_RSP): + trace_seq_printf(p, ": Provision Discovery Response"); + break; + default: + trace_seq_printf(p, "Unknown p2p %d", p2p); + break; + } + } else { + switch (cat) { + case 0: + trace_seq_printf(p, ":Spectrum %d", type); + break; + case 1: + trace_seq_printf(p, ":QOS %d", type); + break; + case 2: + trace_seq_printf(p, ":DLS %d", type); + break; + case 3: + trace_seq_printf(p, ":BA %d", type); + break; + case 4: + trace_seq_printf(p, ":Public %d", type); + break; + case 5: + trace_seq_printf(p, ":Radio Measure %d", type); + break; + case 6: + trace_seq_printf(p, ":Fast BSS %d", type); + break; + case 7: + trace_seq_printf(p, ":HT Action %d", type); + break; + case 8: + trace_seq_printf(p, ":SA Query %d", type); + break; + case 9: + trace_seq_printf(p, ":Protected Public %d", type); + break; + case 10: + trace_seq_printf(p, ":WNM %d", type); + break; + case 11: + trace_seq_printf(p, ":Unprotected WNM %d", type); + break; + case 12: + trace_seq_printf(p, ":TDLS %d", type); + break; + case 13: + trace_seq_printf(p, ":Mesh %d", type); + break; + case 14: + trace_seq_printf(p, ":MultiHop %d", type); + break; + case 15: + trace_seq_printf(p, ":Self Protected %d", type); + break; + case 126: + trace_seq_printf(p, ":Vendor protected"); + break; + case 127: + trace_seq_printf(p, ":Vendor"); + break; + default: + trace_seq_printf(p, ":Unknown category %d", cat); + break; + } + } + break; + default: + trace_seq_printf(p, "Unknown subtype %d", + frame_control & IEEE80211_FCTL_STYPE); + break; + } + + trace_seq_putc(p, 0); + + return ret; +} +#endif /* defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) */ + +#undef __print_mgmt_info +#define __print_mgmt_info(frame_control, cat, type, p2p) \ + ftrace_print_mgmt_info(p, frame_control, cat, type, p2p) + +TRACE_EVENT(roc, TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration), + TP_ARGS(vif_idx, freq, duration), + TP_STRUCT__entry(__field(u8, vif_idx) __field(u16, freq) + __field(unsigned int, duration)), + TP_fast_assign(__entry->vif_idx = vif_idx; __entry->freq = freq; + __entry->duration = duration;), + TP_printk("f=%d vif=%d dur=%d", __entry->freq, __entry->vif_idx, + __entry->duration)); + +TRACE_EVENT(cancel_roc, TP_PROTO(u8 vif_idx), TP_ARGS(vif_idx), + TP_STRUCT__entry(__field(u8, vif_idx)), + TP_fast_assign(__entry->vif_idx = vif_idx;), + TP_printk("vif=%d", __entry->vif_idx)); + +TRACE_EVENT(roc_exp, TP_PROTO(u8 vif_idx), TP_ARGS(vif_idx), + TP_STRUCT__entry(__field(u8, vif_idx)), + TP_fast_assign(__entry->vif_idx = vif_idx;), + TP_printk("vif=%d", __entry->vif_idx)); + +TRACE_EVENT(switch_roc, TP_PROTO(u8 vif_idx), TP_ARGS(vif_idx), + TP_STRUCT__entry(__field(u8, vif_idx)), + TP_fast_assign(__entry->vif_idx = vif_idx;), + TP_printk("vif=%d", __entry->vif_idx)); + +DECLARE_EVENT_CLASS(mgmt_template, + TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt), + TP_ARGS(freq, vif_idx, sta_idx, mgmt), + TP_STRUCT__entry(__field(u16, freq) __field(u8, vif_idx) + __field(u8, sta_idx) __field(u16, frame_control) + __field(u8, action_cat) __field(u8, action_type) + __field(u8, action_p2p)), + TP_fast_assign(__entry->freq = freq; __entry->vif_idx = vif_idx; + __entry->sta_idx = sta_idx; + __entry->frame_control = le16_to_cpu(mgmt->frame_control); + __entry->action_cat = mgmt->u.action.category; + __entry->action_type = mgmt->u.action.action_code; + __entry->action_p2p = *((u8 *)&mgmt->u.action.category + + MGMT_ACTION_OUI_SUBTYPE_OFFSET);), + TP_printk("f=%d vif=%d sta=%d -> %s", __entry->freq, __entry->vif_idx, + __entry->sta_idx, + __print_mgmt_info(__entry->frame_control, __entry->action_cat, + __entry->action_type, __entry->action_p2p))); + +DEFINE_EVENT(mgmt_template, mgmt_tx, + TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, + struct ieee80211_mgmt *mgmt), + TP_ARGS(freq, vif_idx, sta_idx, mgmt)); + +DEFINE_EVENT(mgmt_template, mgmt_rx, + TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, + struct ieee80211_mgmt *mgmt), + TP_ARGS(freq, vif_idx, sta_idx, mgmt)); + +TRACE_EVENT(mgmt_cfm, TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked), + TP_ARGS(vif_idx, sta_idx, acked), + TP_STRUCT__entry(__field(u8, vif_idx) __field(u8, sta_idx) + __field(bool, acked)), + TP_fast_assign(__entry->vif_idx = vif_idx; + __entry->sta_idx = sta_idx; __entry->acked = acked;), + TP_printk("vif=%d sta=%d ack=%d", __entry->vif_idx, + __entry->sta_idx, __entry->acked)); +#endif /* CONFIG_RWNX_FULLMAC */ + +/***************************************************************************** + * TRACE function for TXQ + ****************************************************************************/ + +#if defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) && \ + !defined(TRACE_HEADER_MULTI_READ) + +#include +#include + +static inline const char *ftrace_print_txq(struct trace_seq *p, int txq_idx) +{ + const char *ret = trace_seq_buffer_ptr(p); + + if (txq_idx == TXQ_INACTIVE) { + trace_seq_printf(p, "[INACTIVE]"); + } else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) { + trace_seq_printf(p, "[STA %d/%d]", txq_idx / NX_NB_TXQ_PER_STA, + txq_idx % NX_NB_TXQ_PER_STA); +#ifdef CONFIG_RWNX_FULLMAC + } else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) { + trace_seq_printf(p, "[BC/MC %d]", txq_idx - NX_FIRST_BCMC_TXQ_IDX); + } else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) { + trace_seq_printf(p, "[UNKNOWN %d]", txq_idx - NX_FIRST_UNK_TXQ_IDX); + } else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) { + trace_seq_printf(p, "[OFFCHAN]"); +#else + } else if (txq_idx < NX_NB_TXQ) { + txq_idx -= NX_FIRST_VIF_TXQ_IDX; + trace_seq_printf(p, "[VIF %d/%d]", txq_idx / NX_NB_TXQ_PER_VIF, + txq_idx % NX_NB_TXQ_PER_VIF); +#endif + } else { + trace_seq_printf(p, "[ERROR %d]", txq_idx); + } + + trace_seq_putc(p, 0); + + return ret; +} + +static inline const char *ftrace_print_sta(struct trace_seq *p, int sta_idx) +{ + const char *ret = trace_seq_buffer_ptr(p); + + if (sta_idx < NX_REMOTE_STA_MAX) + trace_seq_printf(p, "[STA %d]", sta_idx); + else + trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX); + + trace_seq_putc(p, 0); + + return ret; +} + +static inline const char *ftrace_print_hwq(struct trace_seq *p, int hwq_idx) +{ + static const struct trace_print_flags symbols[] = {{RWNX_HWQ_BK, "BK"}, + {RWNX_HWQ_BE, "BE"}, + {RWNX_HWQ_VI, "VI"}, + {RWNX_HWQ_VO, "VO"}, +#ifdef CONFIG_RWNX_FULLMAC + {RWNX_HWQ_BCMC, "BCMC"}, +#else + {RWNX_HWQ_BCN, "BCN"}, +#endif + {-1, NULL}}; + return trace_print_symbols_seq(p, hwq_idx, symbols, ARRAY_SIZE(symbols)); +} + +static inline const char *ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred) +{ + const char *ret = trace_seq_buffer_ptr(p); + +#if CONFIG_USER_MAX == 1 + trace_seq_printf(p, "%d", cred[0]); +#else + int i; + + for (i = 0; i < CONFIG_USER_MAX - 1; i++) + trace_seq_printf(p, "%d-", cred[i]); + trace_seq_printf(p, "%d", cred[i]); +#endif + + trace_seq_putc(p, 0); + return ret; +} + +static inline const char *ftrace_print_mu_info(struct trace_seq *p, u8 mu_info) +{ + const char *ret = trace_seq_buffer_ptr(p); + + if (mu_info) + trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6)); + + trace_seq_putc(p, 0); + return ret; +} + +static inline const char *ftrace_print_mu_group(struct trace_seq *p, int nb_user, + u8 *users) +{ + const char *ret = trace_seq_buffer_ptr(p); + int i; + + if (users[0] != 0xff) + trace_seq_printf(p, "(%d", users[0]); + else + trace_seq_printf(p, "(-"); + for (i = 1; i < CONFIG_USER_MAX; i++) { + if (users[i] != 0xff) + trace_seq_printf(p, ",%d", users[i]); + else + trace_seq_printf(p, ",-"); + } + + trace_seq_printf(p, ")"); + trace_seq_putc(p, 0); + return ret; +} + +static inline const char *ftrace_print_amsdu(struct trace_seq *p, u16 nb_pkt) +{ + const char *ret = trace_seq_buffer_ptr(p); + + if (nb_pkt > 1) + trace_seq_printf(p, "(AMSDU %d)", nb_pkt); + + trace_seq_putc(p, 0); + return ret; +} +#endif /* defined(CONFIG_TRACEPOINTS) && defined(AIC8800_CREATE_TRACE_POINTS) */ + +#undef __print_txq +#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx) + +#undef __print_sta +#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx) + +#undef __print_hwq +#define __print_hwq(hwq) ftrace_print_hwq(p, hwq) + +#undef __print_hwq_cred +#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred) + +#undef __print_mu_info +#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info) + +#undef __print_mu_group +#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users) + +#undef __print_amsdu +#define __print_amsdu(nb_pkt) ftrace_print_amsdu(p, nb_pkt) + +#ifdef CONFIG_RWNX_FULLMAC + +TRACE_EVENT(txq_select, TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb), + TP_ARGS(txq_idx, pkt_ready_up, skb), + TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, pkt_ready) + __field(struct sk_buff *, skb)), + TP_fast_assign(__entry->txq_idx = txq_idx; + __entry->pkt_ready = pkt_ready_up; __entry->skb = skb;), + TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx), + __entry->pkt_ready, __entry->skb)); + +#endif /* CONFIG_RWNX_FULLMAC */ + +DECLARE_EVENT_CLASS(hwq_template, TP_PROTO(u8 hwq_idx), TP_ARGS(hwq_idx), + TP_STRUCT__entry(__field(u8, hwq_idx)), + TP_fast_assign(__entry->hwq_idx = hwq_idx;), + TP_printk("%s", __print_hwq(__entry->hwq_idx))); + +DEFINE_EVENT(hwq_template, hwq_flowctrl_stop, TP_PROTO(u8 hwq_idx), + TP_ARGS(hwq_idx)); + +DEFINE_EVENT(hwq_template, hwq_flowctrl_start, TP_PROTO(u8 hwq_idx), + TP_ARGS(hwq_idx)); + +DECLARE_EVENT_CLASS(txq_template, TP_PROTO(struct rwnx_txq *txq), TP_ARGS(txq), + TP_STRUCT__entry(__field(u16, txq_idx)), + TP_fast_assign(__entry->txq_idx = txq->idx;), + TP_printk("%s", __print_txq(__entry->txq_idx))); + +DEFINE_EVENT(txq_template, txq_add_to_hw, TP_PROTO(struct rwnx_txq *txq), + TP_ARGS(txq)); + +DEFINE_EVENT(txq_template, txq_del_from_hw, TP_PROTO(struct rwnx_txq *txq), + TP_ARGS(txq)); + +#ifdef CONFIG_RWNX_FULLMAC + +DEFINE_EVENT(txq_template, txq_flowctrl_stop, TP_PROTO(struct rwnx_txq *txq), + TP_ARGS(txq)); + +DEFINE_EVENT(txq_template, txq_flowctrl_restart, TP_PROTO(struct rwnx_txq *txq), + TP_ARGS(txq)); + +#endif /* CONFIG_RWNX_FULLMAC */ + +TRACE_EVENT(process_txq, TP_PROTO(struct rwnx_txq *txq), TP_ARGS(txq), + TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, len) + __field(u16, len_retry) __field(s8, credit) +#ifdef CONFIG_RWNX_FULLMAC + __field(u16, limit) +#endif /* CONFIG_RWNX_FULLMAC */ + ), + TP_fast_assign(__entry->txq_idx = txq->idx; + __entry->len = skb_queue_len(&txq->sk_list); +#ifdef CONFIG_MAC80211_TXQ + __entry->len += txq->nb_ready_mac80211; +#endif + __entry->len_retry = txq->nb_retry; + __entry->credit = txq->credits; +#ifdef CONFIG_RWNX_FULLMAC + __entry->limit = txq->push_limit; +#endif /* CONFIG_RWNX_FULLMAC */ + ), +#ifdef CONFIG_RWNX_FULLMAC + TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d", + __print_txq(__entry->txq_idx), __entry->credit, + __entry->len, __entry->len_retry, __entry->limit) +#else + TP_printk("%s txq_credits=%d, len=%d, retry_len=%d", + __print_txq(__entry->txq_idx), __entry->credit, + __entry->len, __entry->len_retry) +#endif /* CONFIG_RWNX_FULLMAC */ +); + +DECLARE_EVENT_CLASS(txq_reason_template, TP_PROTO(struct rwnx_txq *txq, u16 reason), + TP_ARGS(txq, reason), + TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, reason) + __field(u16, status)), + TP_fast_assign(__entry->txq_idx = txq->idx; __entry->reason = reason; + __entry->status = txq->status;), + TP_printk("%s reason=%s status=%s", __print_txq(__entry->txq_idx), + __print_symbolic(__entry->reason, {RWNX_TXQ_STOP_FULL, "FULL"}, + {RWNX_TXQ_STOP_CSA, "CSA"}, + {RWNX_TXQ_STOP_STA_PS, "PS"}, + {RWNX_TXQ_STOP_VIF_PS, "VPS"}, + {RWNX_TXQ_STOP_CHAN, "CHAN"}, + {RWNX_TXQ_STOP_MU_POS, "MU"}), + __print_flags(__entry->status, "|", + {RWNX_TXQ_IN_HWQ_LIST, "IN LIST"}, + {RWNX_TXQ_STOP_FULL, "FULL"}, + {RWNX_TXQ_STOP_CSA, "CSA"}, + {RWNX_TXQ_STOP_STA_PS, "PS"}, + {RWNX_TXQ_STOP_VIF_PS, "VPS"}, + {RWNX_TXQ_STOP_CHAN, "CHAN"}, + {RWNX_TXQ_STOP_MU_POS, "MU"}, + {RWNX_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"}))); + +DEFINE_EVENT(txq_reason_template, txq_start, + TP_PROTO(struct rwnx_txq *txq, u16 reason), TP_ARGS(txq, reason)); + +DEFINE_EVENT(txq_reason_template, txq_stop, + TP_PROTO(struct rwnx_txq *txq, u16 reason), TP_ARGS(txq, reason)); + +TRACE_EVENT(push_desc, + TP_PROTO(struct sk_buff *skb, struct rwnx_sw_txhdr *sw_txhdr, int push_flags), + TP_ARGS(skb, sw_txhdr, push_flags), + TP_STRUCT__entry(__field(struct sk_buff *, skb) __field(unsigned int, len) + __field(u16, tx_queue) __field(u8, hw_queue) + __field(u8, push_flag) __field(u32, flag) + __field(s8, txq_cred) __field(u8, hwq_cred) + __field(u16, pkt_cnt) + __field(u8, mu_info)), + TP_fast_assign(__entry->skb = skb; __entry->tx_queue = sw_txhdr->txq->idx; + __entry->push_flag = push_flags; + __entry->hw_queue = sw_txhdr->txq->hwq->id; + __entry->txq_cred = sw_txhdr->txq->credits; +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + __entry->pkt_cnt = sw_txhdr->desc.host.packet_cnt; +#endif +#ifdef CONFIG_RWNX_FULLMAC + __entry->flag = sw_txhdr->desc.host.flags; +#ifdef CONFIG_RWNX_SPLIT_TX_BUF +#ifdef CONFIG_RWNX_AMSDUS_TX + if (sw_txhdr->amsdu.len) + __entry->len = sw_txhdr->amsdu.len; + else +#endif /* CONFIG_RWNX_AMSDUS_TX */ + __entry->len = sw_txhdr->desc.host.packet_len[0]; +#else + __entry->len = sw_txhdr->desc.host.packet_len; +#endif /* CONFIG_RWNX_SPLIT_TX_BUF */ +#else /* !CONFIG_RWNX_FULLMAC */ + __entry->flag = sw_txhdr->desc.umac.flags; + __entry->len = sw_txhdr->frame_len; + __entry->sn = sw_txhdr->sn; +#endif /* CONFIG_RWNX_FULLMAC */ +#ifdef CONFIG_RWNX_MUMIMO_TX + __entry->mu_info = sw_txhdr->desc.host.mumimo_info; +#else + __entry->mu_info = 0; +#endif + ), +#ifdef CONFIG_RWNX_FULLMAC + TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%s %s %s %s", + __print_txq(__entry->tx_queue), __entry->skb, __entry->len, + __print_hwq(__entry->hw_queue), __entry->txq_cred, + __entry->hwq_cred, __print_mu_info(__entry->mu_info), + __print_flags(__entry->flag, "|", {TXU_CNTRL_RETRY, "RETRY"}, + {TXU_CNTRL_MORE_DATA, "MOREDATA"}, {TXU_CNTRL_MGMT, "MGMT"}, + {TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"}, + {TXU_CNTRL_MGMT_ROBUST, "ROBUST"}, {TXU_CNTRL_AMSDU, "AMSDU"}, + {TXU_CNTRL_USE_4ADDR, "4ADDR"}, {TXU_CNTRL_EOSP, "EOSP"}, + {TXU_CNTRL_MESH_FWD, "MESH_FWD"}, {TXU_CNTRL_TDLS, "TDLS"}), + (__entry->push_flag & RWNX_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "", + (!(__entry->flag & TXU_CNTRL_RETRY) && + (__entry->push_flag & RWNX_PUSH_RETRY)) + ? "(SW_RETRY)" : "", + __print_amsdu(__entry->pkt_cnt)) +#else + TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d %s", + __print_txq(__entry->tx_queue), __entry->skb, __entry->len, + __print_hwq(__entry->hw_queue), __entry->txq_cred, + __entry->hwq_cred, __print_mu_info(__entry->mu_info), + __entry->flag, + __print_flags(__entry->push_flag, "|", {RWNX_PUSH_RETRY, "RETRY"}, + {RWNX_PUSH_IMMEDIATE, "IMMEDIATE"}), + __entry->sn, __print_amsdu(__entry->pkt_cnt)) +#endif /* CONFIG_RWNX_FULLMAC */ +); + +TRACE_EVENT(txq_queue_skb, TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, bool retry), + TP_ARGS(skb, txq, retry), + TP_STRUCT__entry(__field(struct sk_buff *, skb) __field(u16, txq_idx) + __field(s8, credit) __field(u16, q_len) + __field(u16, q_len_retry) __field(bool, retry)), + TP_fast_assign(__entry->skb = skb; __entry->txq_idx = txq->idx; + __entry->credit = txq->credits; + __entry->q_len = skb_queue_len(&txq->sk_list); + __entry->q_len_retry = txq->nb_retry; + __entry->retry = retry;), + TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)", + __print_txq(__entry->txq_idx), __entry->skb, __entry->retry, + __entry->credit, __entry->q_len, __entry->q_len_retry)); + +#ifdef CONFIG_MAC80211_TXQ +TRACE_EVENT(txq_wake, TP_PROTO(struct rwnx_txq *txq), TP_ARGS(txq), + TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, q_len)), + TP_fast_assign(__entry->txq_idx = txq->idx; + __entry->q_len = txq->nb_ready_mac80211;), + TP_printk("%s mac80211_queue_len=%d", __print_txq(__entry->txq_idx), + __entry->q_len)); + +TRACE_EVENT(txq_drop, TP_PROTO(struct rwnx_txq *txq, unsigned long nb_drop), + TP_ARGS(txq, nb_drop), + TP_STRUCT__entry(__field(u16, txq_idx) __field(u16, nb_drop)), + TP_fast_assign(__entry->txq_idx = txq->idx; + __entry->nb_drop = nb_drop;), + TP_printk("%s %u pkt have been dropped by codel in mac80211 txq", + __print_txq(__entry->txq_idx), __entry->nb_drop)); + +#endif + +DECLARE_EVENT_CLASS(idx_template, TP_PROTO(u16 idx), TP_ARGS(idx), + TP_STRUCT__entry(__field(u16, idx)), + TP_fast_assign(__entry->idx = idx;), + TP_printk("idx=%d", __entry->idx)); + +DEFINE_EVENT(idx_template, txq_vif_start, TP_PROTO(u16 idx), TP_ARGS(idx)); + +DEFINE_EVENT(idx_template, txq_vif_stop, TP_PROTO(u16 idx), TP_ARGS(idx)); + +TRACE_EVENT(process_hw_queue, TP_PROTO(struct rwnx_hwq *hwq), TP_ARGS(hwq), + TP_STRUCT__entry(__field(u16, hwq) + __array(u8, credits, CONFIG_USER_MAX)), + TP_fast_assign(__entry->hwq = hwq->id;), + TP_printk("hw_queue=%s hw_credits=%s", __print_hwq(__entry->hwq), + __print_hwq_cred(__entry->credits))); + +DECLARE_EVENT_CLASS(sta_idx_template, TP_PROTO(u16 idx), TP_ARGS(idx), + TP_STRUCT__entry(__field(u16, idx)), + TP_fast_assign(__entry->idx = idx;), + TP_printk("%s", __print_sta(__entry->idx))); + +DEFINE_EVENT(sta_idx_template, txq_sta_start, TP_PROTO(u16 idx), TP_ARGS(idx)); + +DEFINE_EVENT(sta_idx_template, txq_sta_stop, TP_PROTO(u16 idx), TP_ARGS(idx)); + +#ifdef CONFIG_RWNX_FULLMAC + +DEFINE_EVENT(sta_idx_template, ps_disable, TP_PROTO(u16 idx), TP_ARGS(idx)); + +#endif /* CONFIG_RWNX_FULLMAC */ + +TRACE_EVENT(skb_confirm, + #ifdef CONFIG_RWNX_FULLMAC + TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, + struct rwnx_hwq *hwq, struct tx_cfm_tag *cfm), + #else + TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, + struct rwnx_hwq *hwq, u8 cfm), + #endif + TP_ARGS(skb, txq, hwq, cfm), + TP_STRUCT__entry(__field(struct sk_buff *, skb) __field(u16, txq_idx) + __field(u8, hw_queue) __array(u8, hw_credit, CONFIG_USER_MAX) + __field(s8, sw_credit) __field(s8, sw_credit_up) +#ifdef CONFIG_RWNX_FULLMAC + __field(u8, ampdu_size) +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + __field(u16, amsdu) +#endif /* CONFIG_RWNX_SPLIT_TX_BUF */ + __field(u16, sn) +#endif /* CONFIG_RWNX_FULLMAC */ + ), + TP_fast_assign(__entry->skb = skb; __entry->txq_idx = txq->idx; + __entry->hw_queue = hwq->id; + __entry->sw_credit = txq->credits; +#if defined CONFIG_RWNX_FULLMAC + __entry->sw_credit_up = cfm->credits; + __entry->ampdu_size = cfm->ampdu_size; +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + __entry->amsdu = cfm->amsdu_size; __entry->sn = cfm->sn; +#endif +#else + __entry->sw_credit_up = cfm +#endif /* CONFIG_RWNX_FULLMAC */ + ), + TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)" +#ifdef CONFIG_RWNX_FULLMAC + " sn=%u ampdu=%d" +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + " amsdu=%u" +#endif +#endif + , + __print_txq(__entry->txq_idx), __entry->skb, + __print_hwq(__entry->hw_queue), + __print_hwq_cred(__entry->hw_credit), __entry->sw_credit, + __entry->sw_credit_up +#ifdef CONFIG_RWNX_FULLMAC + , + __entry->sn, __entry->ampdu_size +#ifdef CONFIG_RWNX_SPLIT_TX_BUF + , + __entry->amsdu +#endif +#endif + )); + +TRACE_EVENT(credit_update, TP_PROTO(struct rwnx_txq *txq, s8 cred_up), + TP_ARGS(txq, cred_up), + TP_STRUCT__entry(__field(struct sk_buff *, skb) + __field(u16, txq_idx) __field(s8, sw_credit) + __field(s8, sw_credit_up)), + TP_fast_assign(__entry->txq_idx = txq->idx; + __entry->sw_credit = txq->credits; + __entry->sw_credit_up = cred_up;), + TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx), + __entry->sw_credit, __entry->sw_credit_up)) +#ifdef CONFIG_RWNX_FULLMAC +DECLARE_EVENT_CLASS(ps_template, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta), + TP_STRUCT__entry(__field(u16, idx) __field(u16, ready_ps) + __field(u16, sp_ps) __field(u16, ready_uapsd) + __field(u16, sp_uapsd)), + TP_fast_assign(__entry->idx = sta->sta_idx; + __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID]; + __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID]; + __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID]; + __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];), + TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d", + __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps, + __entry->ready_uapsd, __entry->sp_uapsd)); + +DEFINE_EVENT(ps_template, ps_queue, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta)); + +DEFINE_EVENT(ps_template, ps_push, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta)); + +DEFINE_EVENT(ps_template, ps_enable, TP_PROTO(struct rwnx_sta *sta), TP_ARGS(sta)); + +TRACE_EVENT(ps_traffic_update, TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd), + TP_ARGS(sta_idx, traffic, uapsd), + TP_STRUCT__entry(__field(u16, sta_idx) __field(u8, traffic) + __field(bool, uapsd)), + TP_fast_assign(__entry->sta_idx = sta_idx; + __entry->traffic = traffic; __entry->uapsd = uapsd;), + TP_printk("%s %s%s traffic available ", + __print_sta(__entry->sta_idx), + __entry->traffic ? "" : "no more ", + __entry->uapsd ? "U-APSD" : "legacy PS")); + +TRACE_EVENT(ps_traffic_req, TP_PROTO(struct rwnx_sta *sta, u16 pkt_req, u8 ps_id), + TP_ARGS(sta, pkt_req, ps_id), + TP_STRUCT__entry(__field(u16, idx) __field(u16, pkt_req) + __field(u8, ps_id) __field(u16, ready) + __field(u16, sp)), + TP_fast_assign(__entry->idx = sta->sta_idx; + __entry->pkt_req = pkt_req; __entry->ps_id = ps_id; + __entry->ready = sta->ps.pkt_ready[ps_id]; + __entry->sp = sta->ps.sp_cnt[ps_id];), + TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)", + __print_sta(__entry->idx), + __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS", + __entry->pkt_req, __entry->ready, __entry->sp)); + +#ifdef CONFIG_RWNX_AMSDUS_TX +TRACE_EVENT(amsdu_subframe, TP_PROTO(struct rwnx_sw_txhdr *sw_txhdr), + TP_ARGS(sw_txhdr), + TP_STRUCT__entry(__field(struct sk_buff *, skb) + __field(u16, txq_idx) __field(u8, nb) + __field(u32, len)), + TP_fast_assign(__entry->skb = sw_txhdr->skb; + __entry->nb = sw_txhdr->amsdu.nb; + __entry->len = sw_txhdr->amsdu.len; + __entry->txq_idx = sw_txhdr->txq->idx;), + TP_printk("%s skb=%p %s nb_subframe=%d, len=%u", + __print_txq(__entry->txq_idx), __entry->skb, + (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe", + __entry->nb, __entry->len)); +#endif + +#endif /* CONFIG_RWNX_FULLMAC */ + +#ifdef CONFIG_RWNX_MUMIMO_TX +TRACE_EVENT(mu_group_update, TP_PROTO(struct rwnx_mu_group *group), + TP_ARGS(group), + TP_STRUCT__entry(__field(u8, nb_user) __field(u8, group_id) + __array(u8, users, CONFIG_USER_MAX)), + TP_fast_assign(int i; __entry->nb_user = group->user_cnt; + for (i = 0; i < CONFIG_USER_MAX; i++) { + if (group->users[i]) + __entry->users[i] = group->users[i]->sta_idx; + else + __entry->users[i] = 0xff; + } + __entry->group_id = group->group_id;), + TP_printk("Group-id = %d, Users = %s", __entry->group_id, + __print_mu_group(__entry->nb_user, __entry->users))); + +TRACE_EVENT(mu_group_delete, TP_PROTO(int group_id), TP_ARGS(group_id), + TP_STRUCT__entry(__field(u8, group_id)), + TP_fast_assign(__entry->group_id = group_id;), + TP_printk("Group-id = %d", __entry->group_id)); + +TRACE_EVENT(mu_group_selection, TP_PROTO(struct rwnx_sta *sta, int group_id), + TP_ARGS(sta, group_id), + TP_STRUCT__entry(__field(u8, sta_idx) __field(u8, group_id)), + TP_fast_assign(__entry->sta_idx = sta->sta_idx; + __entry->group_id = group_id;), + TP_printk("[Sta %d] Group-id = %d", __entry->sta_idx, + __entry->group_id)); + +TRACE_EVENT(txq_select_mu_group, + TP_PROTO(struct rwnx_txq *txq, int group_id, int pos), + TP_ARGS(txq, group_id, pos), + TP_STRUCT__entry(__field(u16, txq_idx) __field(u8, group_id) + __field(u8, pos)), + TP_fast_assign(__entry->txq_idx = txq->idx; + __entry->group_id = group_id; __entry->pos = pos;), + TP_printk("%s: group=%d pos=%d", __print_txq(__entry->txq_idx), + __entry->group_id, __entry->pos)); + +#endif /* CONFIG_RWNX_MUMIMO_TX */ + +/***************************************************************************** + * TRACE functions for MESH + ****************************************************************************/ +#ifdef CONFIG_RWNX_FULLMAC +DECLARE_EVENT_CLASS(mesh_path_template, TP_PROTO(struct rwnx_mesh_path *mesh_path), + TP_ARGS(mesh_path), + TP_STRUCT__entry(__field(u8, idx) __field(u8, next_hop_sta) + __array(u8, tgt_mac, ETH_ALEN)), + TP_fast_assign(__entry->idx = mesh_path->path_idx; + memcpy(__entry->tgt_mac, &mesh_path->tgt_mac_addr, ETH_ALEN); + if (mesh_path->p_nhop_sta) + __entry->next_hop_sta = mesh_path->p_nhop_sta->sta_idx; + else + __entry->next_hop_sta = 0xff;), + TP_printk("Mpath(%d): target=%pM next_hop=STA-%d", __entry->idx, + __entry->tgt_mac, __entry->next_hop_sta)); + +DEFINE_EVENT(mesh_path_template, mesh_create_path, + TP_PROTO(struct rwnx_mesh_path *mesh_path), TP_ARGS(mesh_path)); + +DEFINE_EVENT(mesh_path_template, mesh_delete_path, + TP_PROTO(struct rwnx_mesh_path *mesh_path), TP_ARGS(mesh_path)); + +DEFINE_EVENT(mesh_path_template, mesh_update_path, + TP_PROTO(struct rwnx_mesh_path *mesh_path), TP_ARGS(mesh_path)); + +#endif /* CONFIG_RWNX_FULLMAC */ + +/***************************************************************************** + * TRACE functions for RADAR + ****************************************************************************/ +#ifdef CONFIG_RWNX_RADAR +TRACE_EVENT(radar_pulse, TP_PROTO(u8 chain, struct radar_pulse *pulse), + TP_ARGS(chain, pulse), + TP_STRUCT__entry(__field(u8, chain) __field(s16, freq) + __field(u16, pri) __field(u8, len) + __field(u8, fom)), + TP_fast_assign(__entry->freq = pulse->freq * 2; + __entry->len = pulse->len * 2; + __entry->fom = pulse->fom * 6; + __entry->pri = pulse->rep; __entry->chain = chain;), + TP_printk("%s: PRI=%.5d LEN=%.3d FOM=%.2d%% freq=%dMHz ", + __print_symbolic(__entry->chain, {RWNX_RADAR_RIU, "RIU"}, + {RWNX_RADAR_FCU, "FCU"}), + __entry->pri, __entry->len, __entry->fom, __entry->freq)); + +TRACE_EVENT(radar_detected, TP_PROTO(u8 chain, u8 region, s16 freq, u8 type, u16 pri), + TP_ARGS(chain, region, freq, type, pri), + TP_STRUCT__entry(__field(u8, chain) __field(u8, region) __field(s16, freq) + __field(u8, type) __field(u16, pri)), + TP_fast_assign(__entry->chain = chain; __entry->region = region; + __entry->freq = freq; __entry->type = type; + __entry->pri = pri;), + TP_printk("%s: region=%s type=%d freq=%dMHz (pri=%dus)", + __print_symbolic(__entry->chain, {RWNX_RADAR_RIU, "RIU"}, + {RWNX_RADAR_FCU, "FCU"}), + __print_symbolic(__entry->region, {NL80211_DFS_UNSET, "UNSET"}, + {NL80211_DFS_FCC, "FCC"}, + {NL80211_DFS_ETSI, "ETSI"}, + {NL80211_DFS_JP, "JP"}), + __entry->type, __entry->freq, __entry->pri)); + +TRACE_EVENT(radar_set_region, TP_PROTO(u8 region), TP_ARGS(region), + TP_STRUCT__entry(__field(u8, region)), + TP_fast_assign(__entry->region = region;), + TP_printk("region=%s", + __print_symbolic(__entry->region, + {NL80211_DFS_UNSET, "UNSET"}, + {NL80211_DFS_FCC, "FCC"}, + {NL80211_DFS_ETSI, "ETSI"}, + {NL80211_DFS_JP, "JP"}))); + +TRACE_EVENT(radar_enable_detection, TP_PROTO(u8 region, u8 enable, u8 chain), + TP_ARGS(region, enable, chain), + TP_STRUCT__entry(__field(u8, region) __field(u8, chain) + __field(u8, enable)), + TP_fast_assign(__entry->chain = chain; __entry->enable = enable; + __entry->region = region;), + TP_printk("%s: %s radar detection %s", + __print_symbolic(__entry->chain, {RWNX_RADAR_RIU, "RIU"}, + {RWNX_RADAR_FCU, "FCU"}), + __print_symbolic(__entry->enable, + {RWNX_RADAR_DETECT_DISABLE, "Disable"}, + {RWNX_RADAR_DETECT_ENABLE, + "Enable (no report)"}, + {RWNX_RADAR_DETECT_REPORT, "Enable"}), + __entry->enable == RWNX_RADAR_DETECT_DISABLE + ? "" : __print_symbolic(__entry->region, + {NL80211_DFS_UNSET, "UNSET"}, + {NL80211_DFS_FCC, "FCC"}, + {NL80211_DFS_ETSI, "ETSI"}, + {NL80211_DFS_JP, "JP"}))); +#endif /* CONFIG_RWNX_RADAR */ + +/***************************************************************************** + * TRACE functions for IPC message + ****************************************************************************/ +#include "rwnx_strs.h" + +DECLARE_EVENT_CLASS(ipc_msg_template, TP_PROTO(u16 id), TP_ARGS(id), + TP_STRUCT__entry(__field(u16, id)), + TP_fast_assign(__entry->id = id;), + TP_printk("%s (%d - %d)", RWNX_ID2STR(__entry->id), + MSG_T(__entry->id), MSG_I(__entry->id))); + +DEFINE_EVENT(ipc_msg_template, msg_send, TP_PROTO(u16 id), TP_ARGS(id)); + +DEFINE_EVENT(ipc_msg_template, msg_recv, TP_PROTO(u16 id), TP_ARGS(id)); + +#endif /* !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */ + +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH AIC_TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_FILE rwnx_events +#include diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c new file mode 100644 index 0000000000000..994b6f84cdaac --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief IRQ handler + * + ****************************************************************************** + */ +#include + +#include "rwnx_irqs.h" +#include "ipc_host.h" +#include "rwnx_defs.h" +#include "rwnx_prof.h" + +/** + * rwnx_irq_hdlr - IRQ handler + * @irq: Interrupt number + * @dev_id: Driver private data registered for the interrupt + * + * Handler registered by the platform driver + * + * Return: %IRQ_HANDLED after scheduling the interrupt bottom half. + */ +irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id) +{ + struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)dev_id; + + disable_irq_nosync(irq); + tasklet_schedule(&rwnx_hw->task); + return IRQ_HANDLED; +} + +/** + * rwnx_task - Bottom half for IRQ handler + * @data: Driver private data passed to the tasklet + * + * Read irq status and process accordingly + */ +void rwnx_task(unsigned long data) +{ + struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)data; + + spin_lock_bh(&rwnx_hw->tx_lock); + rwnx_hwq_process_all(rwnx_hw); + spin_unlock_bh(&rwnx_hw->tx_lock); +} diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h new file mode 100644 index 0000000000000..ff6c766c727c3 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_irqs.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RWNX_IRQS_H_ +#define _RWNX_IRQS_H_ + +#include + +/* IRQ handler to be registered by platform driver */ +irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id); + +void rwnx_task(unsigned long data); + +#endif /* _RWNX_IRQS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c new file mode 100644 index 0000000000000..cc124c8a34e8b --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.c @@ -0,0 +1,1614 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief RX function definitions + * + ****************************************************************************** + */ + +#include "rwnx_defs.h" +#include "rwnx_prof.h" +#include "rwnx_tx.h" +#include "rwnx_rx.h" +#include +#include +#ifdef CONFIG_RWNX_BFMER +#include "rwnx_bfmer.h" +#endif //(CONFIG_RWNX_BFMER) +#ifdef CONFIG_RWNX_FULLMAC +#include "rwnx_msg_tx.h" +#include "rwnx_tdls.h" +#endif /* CONFIG_RWNX_FULLMAC */ +#include "aicwf_txrxif.h" +#include "aicwf_sdio.h" +#include "rwnx_compat.h" +#include "rwnx_events.h" +#include "rwnx_msg_rx.h" +#include "rwnx_mod_params.h" +#include +#ifdef CONFIG_AIC8800_POWER_LIMIT +#include "aicwf_compat_8800d80.h" +#endif + +void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif); + +static int rwnx_freq_to_idx(struct rwnx_hw *rwnx_hw, int freq) +{ + struct ieee80211_supported_band *sband = NULL; + int band, ch, idx = 0; + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) { +#ifdef CONFIG_RWNX_FULLMAC + sband = rwnx_hw->wiphy->bands[band]; +#endif /* CONFIG_RWNX_FULLMAC */ + if (!sband) + continue; + + for (ch = 0; ch < sband->n_channels; ch++, idx++) { + if (sband->channels[ch].center_freq == freq) + goto exit; + } + } + + WARN_ON_ONCE(1); + +exit: + // Channel has been found, return the index + return idx; +} + +/*************************************************************************** + * Messages from MM task + **************************************************************************/ +static inline int rwnx_rx_chan_pre_switch_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct rwnx_vif *rwnx_vif; + int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index; + + REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT); + +#ifdef CONFIG_RWNX_FULLMAC + list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { + if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) + rwnx_txq_vif_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + } +#endif /* CONFIG_RWNX_FULLMAC */ + + REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT); + + return 0; +} + +static inline int rwnx_rx_chan_switch_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct rwnx_vif *rwnx_vif; + int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index; + bool roc = ((struct mm_channel_switch_ind *)msg->param)->roc; + bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls; + + REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT); + +#ifdef CONFIG_RWNX_FULLMAC + if (roc_tdls) { + u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index; + + list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { + if (rwnx_vif->vif_index == vif_index) { + rwnx_vif->roc_tdls = true; + rwnx_txq_tdls_sta_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + } + } + } else if (!roc) { + list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { + if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) + rwnx_txq_vif_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + } + } else { + /* Retrieve the allocated RoC element */ + struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem; + /* Get VIF on which RoC has been started */ + // rwnx_vif = netdev_priv(roc_elem->wdev->netdev); + + /* For debug purpose (use ftrace kernel option) */ + // trace_switch_roc(rwnx_vif->vif_index); + + if (roc_elem) { + /* If mgmt_roc is true, remain on channel has been started by + * ourself + */ + if (!roc_elem->mgmt_roc) { + /* Inform the host that we have switch on the indicated + * off-channel + */ + cfg80211_ready_on_channel(roc_elem->wdev, + roc_elem->cookie, + roc_elem->chan, roc_elem->duration, + GFP_ATOMIC); + } + + /* Keep in mind that we have switched on the channel */ + roc_elem->on_chan = true; + } else { + AICWFDBG(LOGDEBUG, "roc_elem == null\n"); + } + // Enable traffic on OFF channel queue + rwnx_txq_offchan_start(rwnx_hw); + } + + tasklet_schedule(&rwnx_hw->task); + + rwnx_hw->cur_chanctx = chan_idx; + rwnx_radar_detection_enable_on_cur_channel(rwnx_hw); + +#endif /* CONFIG_RWNX_FULLMAC */ + + REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT); + + return 0; +} + +static inline int rwnx_rx_tdls_chan_switch_cfm(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + return 0; +} + +static inline int rwnx_rx_tdls_chan_switch_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ +#ifdef CONFIG_RWNX_FULLMAC + // Enable traffic on OFF channel queue + rwnx_txq_offchan_start(rwnx_hw); + + return 0; +#endif +} + +static inline int rwnx_rx_tdls_chan_switch_base_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct rwnx_vif *rwnx_vif; + u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + +#ifdef CONFIG_RWNX_FULLMAC + list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { + if (rwnx_vif->vif_index == vif_index) { + rwnx_vif->roc_tdls = false; + rwnx_txq_tdls_sta_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + } + } + return 0; +#endif +} + +static inline int rwnx_rx_tdls_peer_ps_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct rwnx_vif *rwnx_vif; + u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index; + bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on; + +#ifdef CONFIG_RWNX_FULLMAC + list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) { + if (rwnx_vif->vif_index == vif_index) { + rwnx_vif->sta.tdls_sta->tdls.ps_on = ps_on; + // Update PS status for the TDLS station + rwnx_ps_bh_enable(rwnx_hw, rwnx_vif->sta.tdls_sta, ps_on); + } + } + + return 0; +#endif +} + +static inline int rwnx_rx_remain_on_channel_exp_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ +#ifdef CONFIG_RWNX_FULLMAC + /* Retrieve the allocated RoC element */ + struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem; + /* Get VIF on which RoC has been started */ + struct rwnx_vif *rwnx_vif; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (!roc_elem) + return 0; + + rwnx_vif = container_of(roc_elem->wdev, struct rwnx_vif, wdev); + /* For debug purpose (use ftrace kernel option) */ +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_roc_exp(rwnx_vif->vif_index); +#endif + /* If mgmt_roc is true, remain on channel has been started by ourself */ + /* If RoC has been cancelled before we switched on channel, do not call + * cfg80211 + */ + if (!roc_elem->mgmt_roc && roc_elem->on_chan) { + /* Inform the host that off-channel period has expired */ + cfg80211_remain_on_channel_expired(roc_elem->wdev, + roc_elem->cookie, + roc_elem->chan, GFP_ATOMIC); + } + + /* De-init offchannel TX queue */ + rwnx_txq_offchan_deinit(rwnx_vif); + + /* Free the allocated RoC element */ + kfree(roc_elem); + rwnx_hw->roc_elem = NULL; + +#endif /* CONFIG_RWNX_FULLMAC */ + return 0; +} + +static inline int rwnx_rx_p2p_vif_ps_change_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + int vif_idx = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index; + int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state; + struct rwnx_vif *vif_entry; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + +#ifdef CONFIG_RWNX_FULLMAC + vif_entry = rwnx_hw->vif_table[vif_idx]; + + if (vif_entry) + goto found_vif; +#endif /* CONFIG_RWNX_FULLMAC */ + + goto exit; +found_vif: + +#ifdef CONFIG_RWNX_FULLMAC + if (ps_state == MM_PS_MODE_OFF) { + // Start TX queues for provided VIF + rwnx_txq_vif_start(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw); + } else { + // Stop TX queues for provided VIF + rwnx_txq_vif_stop(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw); + } +#endif /* CONFIG_RWNX_FULLMAC */ + +exit: + return 0; +} + +static inline int rwnx_rx_channel_survey_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_channel_survey_ind *ind = + (struct mm_channel_survey_ind *)msg->param; + // Get the channel index + int idx = rwnx_freq_to_idx(rwnx_hw, ind->freq); + // Get the survey + struct rwnx_survey_info *rwnx_survey; + + if (idx > ARRAY_SIZE(rwnx_hw->survey)) + return 0; + + rwnx_survey = &rwnx_hw->survey[idx]; + + // Store the received parameters + rwnx_survey->chan_time_ms = ind->chan_time_ms; + rwnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms; + rwnx_survey->noise_dbm = ind->noise_dbm; + rwnx_survey->filled = (SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY); + + if (ind->noise_dbm != 0) + rwnx_survey->filled |= SURVEY_INFO_NOISE_DBM; + + return 0; +} + +static inline int rwnx_rx_p2p_noa_upd_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + return 0; +} + +static inline int rwnx_rx_rssi_status_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param; + int vif_idx = ind->vif_index; + bool rssi_status = ind->rssi_status; + + struct rwnx_vif *vif_entry; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + +#ifdef CONFIG_RWNX_FULLMAC + vif_entry = rwnx_hw->vif_table[vif_idx]; + if (vif_entry) { + cfg80211_cqm_rssi_notify(vif_entry->ndev, + rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW + : NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + ind->rssi, GFP_ATOMIC); + } +#endif /* CONFIG_RWNX_FULLMAC */ + + return 0; +} + +static inline int rwnx_rx_pktloss_notify_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ +#ifdef CONFIG_RWNX_FULLMAC + struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param; + struct rwnx_vif *vif_entry; + int vif_idx = ind->vif_index; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + vif_entry = rwnx_hw->vif_table[vif_idx]; + if (vif_entry) { + cfg80211_cqm_pktloss_notify(vif_entry->ndev, + (const u8 *)ind->mac_addr.array, + ind->num_packets, GFP_ATOMIC); + } +#endif /* CONFIG_RWNX_FULLMAC */ + + return 0; +} + +static inline int rwnx_apm_staloss_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_apm_staloss_ind *ind = (struct mm_apm_staloss_ind *)msg->param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + memcpy(rwnx_hw->sta_mac_addr, ind->mac_addr, 6); + rwnx_hw->apm_vif_idx = ind->vif_idx; + + queue_work(rwnx_hw->apm_staloss_wq, &rwnx_hw->apm_staloss_work); + + return 0; +} + +static struct sk_buff *arp_80211_to_8023(struct sk_buff *skb, u8 hdrlen) +{ + struct ethhdr *eth; + size_t payload_len; + struct sk_buff *new_skb = NULL; + char *llc; + u8 da[MAC_ADDR_LEN] = {0}; + u8 sa[MAC_ADDR_LEN] = {0}; + + if (skb->len <= hdrlen) { + AICWFDBG(LOGERROR, "%s skb_len err1\n", __func__); + return NULL; + } + + memcpy(da, &skb->data[4], 6); + memcpy(sa, &skb->data[16], 6); + + skb_pull(skb, hdrlen); + llc = skb->data; + if (llc[0] == 0xAA && llc[1] == 0xAA && llc[2] == 0x03) { + if (skb->len < 8) { + AICWFDBG(LOGERROR, "%s skb_len err2\n", __func__); + return NULL; + } + skb_pull(skb, 8); + } + payload_len = skb->len; + + new_skb = __dev_alloc_skb(ETH_HLEN + payload_len, GFP_ATOMIC); + if (!new_skb) { + AICWFDBG(LOGERROR, "%s skb alloc fail\n", __func__); + return NULL; + } + + skb_reserve(new_skb, ETH_HLEN); + skb_put(new_skb, payload_len); + memcpy(new_skb->data, skb->data, payload_len); + + eth = (struct ethhdr *)skb_push(new_skb, ETH_HLEN); + + memcpy(eth->h_dest, da, ETH_ALEN); + memcpy(eth->h_source, sa, ETH_ALEN); + + eth->h_proto = htons(ETH_P_ARP); + + new_skb->protocol = eth->h_proto; + new_skb->dev = skb->dev; + + return new_skb; +} + +static inline int rwnx_arp_announcement_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct rwnx_vif *vif; + struct sk_buff *skb = NULL; + struct sk_buff *new_skb = NULL; + struct hw_rxhdr rxhdr; + int hdrlen = 24; + struct rxu_arp_announcement_ind *ind = (struct rxu_arp_announcement_ind *)msg->param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + //AICWFDBG(LOGDEBUG, "vif: %d, length: %d, offset: %d\n", + // ind->vif_index, ind->length, ind->offset); + + AICWFDBG(LOGDEBUG, "%s, vif: %d, length: %d, offset: %d\n", + __func__, ind->vif_index, ind->length, ind->offset); + + vif = rwnx_hw->vif_table[ind->vif_index]; + rxhdr.flags_is_amsdu = false; + rxhdr.flags_dst_idx = 0xff; + + if (ind->payload[0] & 0x80) + hdrlen += 2; + + switch ((ind->rx_status >> 2) & 0x7) { + case RWNX_RX_HD_DECR_CCMP128: + hdrlen += 8; + break; + case RWNX_RX_HD_DECR_TKIP: + hdrlen += 8; + break; + case RWNX_RX_HD_DECR_WEP: + hdrlen += 4; + break; + case RWNX_RX_HD_DECR_WAPI: + hdrlen += 18; + break; + default: + break; + } + + skb = __dev_alloc_skb(ind->length, GFP_ATOMIC); + if (!skb) { + AICWFDBG(LOGERROR, "%s skb alloc fail\n", __func__); + return -1; + } + skb_put(skb, ind->length); + memcpy(skb->data, ind->payload, ind->length); + skb->dev = vif->ndev; + + new_skb = arp_80211_to_8023(skb, hdrlen); + if (!new_skb) { + AICWFDBG(LOGERROR, "arp_80211_to_8023 fail\n"); + dev_kfree_skb(skb); + return -1; + } + if (!rwnx_rx_data_skb(rwnx_hw, vif, new_skb, &rxhdr)) { + AICWFDBG(LOGERROR, "rwnx_rx_data_skb fail\n"); + dev_kfree_skb(new_skb); + dev_kfree_skb(skb); + return -1; + } + + dev_kfree_skb(skb); + return 0; +} + +static inline int rwnx_rx_csa_counter_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param; + struct rwnx_vif *vif; + bool found = false; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + // Look for VIF entry + list_for_each_entry(vif, &rwnx_hw->vifs, list) { + if (vif->vif_index == ind->vif_index) { + found = true; + break; + } + } + + if (found) { +#ifdef CONFIG_RWNX_FULLMAC + if (vif->ap.csa) + vif->ap.csa->count = ind->csa_count; + else + netdev_err(vif->ndev, "CSA counter update but no active CSA"); + +#endif + } + + return 0; +} + +#ifdef CONFIG_RWNX_FULLMAC +static inline int rwnx_rx_csa_finish_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param; + struct rwnx_vif *vif; + bool found = false; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + // Look for VIF entry + list_for_each_entry(vif, &rwnx_hw->vifs, list) { + if (vif->vif_index == ind->vif_index) { + found = true; + break; + } + } + + if (found) { + if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP || + RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) { + if (vif->ap.csa) { + vif->ap.csa->status = ind->status; + vif->ap.csa->ch_idx = ind->chan_idx; + schedule_work(&vif->ap.csa->work); + } else { + netdev_err(vif->ndev, + "CSA finish indication but no active CSA"); + } + } else { + if (ind->status == 0) { + rwnx_chanctx_unlink(vif); + rwnx_chanctx_link(vif, ind->chan_idx, NULL); + if (rwnx_hw->cur_chanctx == ind->chan_idx) { + rwnx_radar_detection_enable_on_cur_channel(rwnx_hw); + rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + } else { + rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw); + } + } + } + } + + return 0; +} + +static inline int rwnx_rx_csa_traffic_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param; + struct rwnx_vif *vif; + bool found = false; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + // Look for VIF entry + list_for_each_entry(vif, &rwnx_hw->vifs, list) { + if (vif->vif_index == ind->vif_index) { + found = true; + break; + } + } + + if (found) { + if (ind->enable) + rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CSA, rwnx_hw); + else + rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CSA, rwnx_hw); + } + + return 0; +} + +static inline int rwnx_rx_ps_change_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param; + struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx]; + + if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) { + wiphy_err(rwnx_hw->wiphy, "Invalid sta index reported by fw %d\n", + ind->sta_idx); + return 1; + } + + netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev, + "Sta %d, change PS mode to %s", sta->sta_idx, + ind->ps_state ? "ON" : "OFF"); + + if (sta->valid) { + rwnx_ps_bh_enable(rwnx_hw, sta, ind->ps_state); + } else if (rwnx_hw->adding_sta) { + sta->ps.active = ind->ps_state ? true : false; + } else { + if (rwnx_hw->vif_table[sta->vif_idx] && + rwnx_hw->vif_table[sta->vif_idx]->ndev) + netdev_err(rwnx_hw->vif_table[sta->vif_idx]->ndev, + "Ignore PS mode change on invalid sta\n"); + } + + return 0; +} + +static inline int rwnx_rx_traffic_req_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param; + struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx]; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev, + "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt); + + rwnx_ps_bh_traffic_req(rwnx_hw, sta, ind->pkt_cnt, + ind->uapsd ? UAPSD_ID : LEGACY_PS_ID); + + return 0; +} +#endif /* CONFIG_RWNX_FULLMAC */ + +/*************************************************************************** + * Messages from SCANU task + **************************************************************************/ +#ifdef CONFIG_RWNX_FULLMAC + +static inline int rwnx_rx_scanu_start_cfm(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + const struct ieee80211_regdomain *regdomain; +#endif + + RWNX_DBG(RWNX_FN_ENTRY_STR); + +#ifdef CONFIG_AIC8800_SCHED_SCAN + if (rwnx_hw->scan_request && !rwnx_hw->is_sched_scan) +#endif // CONFIG_AIC8800_SCHED_SCAN + { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + cfg80211_scan_done(rwnx_hw->scan_request, &info); + } + +#ifdef CONFIG_AIC8800_SCHED_SCAN + if (rwnx_hw->is_sched_scan) { + AICWFDBG(LOGINFO, "%s cfg80211_sched_scan_results \r\n", __func__); + cfg80211_sched_scan_results(rwnx_hw->scan_request->wiphy, + rwnx_hw->sched_scan_req->reqid); + kfree(rwnx_hw->scan_request); + rwnx_hw->is_sched_scan = false; + } +#endif // CONFIG_AIC8800_SCHED_SCAN + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + if (rwnx_hw->ccode.auto_set && rwnx_hw->ccode.ccode_find) { +#ifdef CONFIG_AIC8800_REGION_PW + schedule_work(&rwnx_hw->region_wk); +#endif +#ifdef CONFIG_AIC8800_POWER_LIMIT + if (!work_pending(&rwnx_hw->pwlt_wk)) + schedule_work(&rwnx_hw->pwlt_wk); +#endif + regdomain = get_regdomain_from_rwnx_db(rwnx_hw->wiphy, rwnx_hw->country_abbr); + rwnx_regulatory_set_wiphy_regd(rwnx_hw->wiphy, regdomain); + rwnx_hw->ccode.ccode_cnt = 0; + rwnx_hw->ccode.ccode_find = false; + rwnx_hw->ccode.ccode_set = true; + } +#endif + + rwnx_hw->scan_request = NULL; + rwnx_hw->scanning = 0; + aicwf_sdio_scan_activity_put(rwnx_hw); + + return 0; +} + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG +static void parse_ssid_from_id(const u8 *ssid_id, char ssid_name[33], u8 *ssid_len) +{ + u8 len; + + if (!ssid_id) + return; + + len = *(ssid_id + 1); + if (len > 32) + len = 32; + + strscpy(ssid_name, ssid_id + 2, len); + ssid_name[len] = '\0'; + *ssid_len = len; + + if (len > 0) + AICWFDBG(LOGDEBUG, "find_ccode ssid: %s\n", ssid_name); + else + AICWFDBG(LOGDEBUG, "find_ccode ssid is null\n"); +} + +static void rwnx_update_country(struct rwnx_hw *rwnx_hw, + const u8 *country_id, + const struct ieee80211_regdomain **regdomain) +{ + if (!country_id) + return; + + if (memcmp(rwnx_hw->country_abbr, country_id + 2, 2) != 0) { + AICWFDBG(LOGINFO, "update ccode of the connected ap\n"); + + strscpy(rwnx_hw->country_abbr, country_id + 2, 2); + + *regdomain = get_regdomain_from_rwnx_db(rwnx_hw->wiphy, rwnx_hw->country_abbr); + +#ifdef CONFIG_AIC8800_REGION_PW + schedule_work(&rwnx_hw->region_wk); +#endif + +#ifdef CONFIG_AIC8800_POWER_LIMIT + if (!work_pending(&rwnx_hw->pwlt_wk)) + schedule_work(&rwnx_hw->pwlt_wk); +#endif + + rwnx_regulatory_set_wiphy_regd(rwnx_hw->wiphy, *regdomain); + } +} +#endif + +static inline int rwnx_rx_scanu_result_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct cfg80211_bss *bss = NULL; + struct ieee80211_channel *chan; + struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload; + u64 tsf; + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + const u8 *country_id; + const u8 *ssid_id; + char ssid_name[33]; + u8 ssid_len; + const struct ieee80211_regdomain *regdomain; +#endif + + + // RWNX_DBG(RWNX_FN_ENTRY_STR); //debug trace + + chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq); + + if (chan) { + struct timespec64 ts; + + ts = ktime_to_timespec64(ktime_get_boottime()); + tsf = (u64)ts.tv_sec * 1000000 + div_u64(ts.tv_nsec, 1000); + mgmt->u.probe_resp.timestamp = cpu_to_le64(tsf); + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + if (rwnx_hw->ccode.auto_set && !rwnx_hw->ccode.ccode_set && + rwnx_hw->ccode.ccode_cnt < MAX_SEARCH_CNT) { + country_id = + cfg80211_find_ie(WLAN_EID_COUNTRY, mgmt->u.beacon.variable, + ind->length - offsetof(struct ieee80211_mgmt, + u.beacon.variable)); + if (country_id) { + if (ind->rssi > rwnx_hw->ccode.ccode_rssi) { + rwnx_hw->ccode.ccode_rssi = ind->rssi; + // AICWFDBG(LOGINFO, "find_ccode, bssid: %pM\n", + // mgmt->bssid); //debug trace + if (!rwnx_hw->ccode.ccode_find) + rwnx_hw->ccode.ccode_find = true; + strscpy(rwnx_hw->country_abbr, country_id + 2, 2); + ssid_id = + cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.beacon.variable, + ind->length - + offsetof(struct ieee80211_mgmt, + u.beacon.variable)); + parse_ssid_from_id(ssid_id, ssid_name, &ssid_len); + } + } + rwnx_hw->ccode.ccode_cnt += 1; + AICWFDBG(LOGDEBUG, "find cnt: %d\n", + rwnx_hw->ccode.ccode_cnt); // debug trace + } else if (rwnx_hw->ccode.auto_set && !rwnx_hw->ccode.ccode_find && + !rwnx_hw->ccode.ccode_set && + rwnx_hw->ccode.ccode_cnt >= MAX_SEARCH_CNT) { + AICWFDBG(LOGINFO, "find cnt over !!!\n"); + strscpy(rwnx_hw->country_abbr, "XX", 2); + rwnx_hw->ccode.ccode_find = true; + } + + if (rwnx_hw->ccode.auto_set && + atomic_read(&rwnx_hw->ccode.link_scan) == 1) { + if (memcmp(&rwnx_hw->ccode.bssid, mgmt->bssid, ETH_ALEN) == 0) { + country_id = + cfg80211_find_ie(WLAN_EID_COUNTRY, + mgmt->u.probe_resp.variable, + ind->length - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); + rwnx_update_country(rwnx_hw, country_id, ®domain); + memset(&rwnx_hw->ccode.bssid, 0, sizeof(rwnx_hw->ccode.bssid)); + } + atomic_set(&rwnx_hw->ccode.link_scan, 0); + rwnx_hw->ccode.ccode_cnt = 0; + } +#endif + bss = + cfg80211_inform_bss_frame(rwnx_hw->wiphy, chan, + (struct ieee80211_mgmt *)ind->payload, + ind->length, ind->rssi * 100, GFP_ATOMIC); + + } + if (bss) + cfg80211_put_bss(rwnx_hw->wiphy, bss); + return 0; +} +#endif /* CONFIG_RWNX_FULLMAC */ + +/*************************************************************************** + * Messages from ME task + **************************************************************************/ +#ifdef CONFIG_RWNX_FULLMAC +static inline int rwnx_rx_me_tkip_mic_failure_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct me_tkip_mic_failure_ind *ind = + (struct me_tkip_mic_failure_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct net_device *dev = rwnx_vif->ndev; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, + (ind->ga ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE), + ind->keyid, (u8 *)&ind->tsc, GFP_ATOMIC); + + return 0; +} + +static inline int rwnx_rx_me_tx_credits_update_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct me_tx_credits_update_ind *ind = + (struct me_tx_credits_update_ind *)msg->param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + rwnx_txq_credit_update(rwnx_hw, ind->sta_idx, ind->tid, ind->credits); + + return 0; +} +#endif /* CONFIG_RWNX_FULLMAC */ + +/*************************************************************************** + * Messages from SM task + **************************************************************************/ +#ifdef CONFIG_RWNX_FULLMAC +static inline int rwnx_rx_sm_connect_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct net_device *dev = rwnx_vif->ndev; + const u8 *req_ie, *rsp_ie; + const u8 *extcap_ie; + const struct ieee_types_extcap *extcap; + struct ieee80211_channel *chan; + const u8 *ht_ie = NULL; + const u8 *vht_ie = NULL; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Retrieve IE addresses and lengths */ + req_ie = (const u8 *)ind->assoc_ie_buf; + rsp_ie = req_ie + ind->assoc_req_ie_len; + + // Fill-in the AP information + AICWFDBG(LOGINFO, "%s ind->status_code:%d \r\n", __func__, + ind->status_code); + + if (ind->status_code == 0) { + struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->ap_idx]; + u8 txq_status; + struct cfg80211_chan_def chandef; + + sta->valid = true; + sta->sta_idx = ind->ap_idx; + sta->ch_idx = ind->ch_idx; + sta->vif_idx = ind->vif_idx; + sta->vlan_idx = sta->vif_idx; + sta->qos = ind->qos; + sta->acm = ind->acm; + sta->ps.active = false; + sta->aid = ind->aid; + sta->band = ind->band; // ind->chan.band; + sta->width = ind->width; // ind->chan.type; + sta->center_freq = ind->center_freq; // ind->chan.prim20_freq; + sta->center_freq1 = ind->center_freq1; // ind->chan.center1_freq; + sta->center_freq2 = ind->center_freq2; // ind->chan.center2_freq; + rwnx_vif->sta.ap = sta; + // ind->chan.prim20_freq); + chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq); + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + if (!rwnx_hw->mod_params->ht_on) + chandef.width = NL80211_CHAN_WIDTH_20_NOHT; + else + chandef.width = chnl2bw[ind->width]; //[ind->chan.type]; + chandef.center_freq1 = ind->center_freq1; // ind->chan.center1_freq; + chandef.center_freq2 = ind->center_freq2; // ind->chan.center2_freq; + rwnx_chanctx_link(rwnx_vif, ind->ch_idx, &chandef); + memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN); + if (ind->ch_idx == rwnx_hw->cur_chanctx) + txq_status = 0; + else + txq_status = RWNX_TXQ_STOP_CHAN; + memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param)); + rwnx_txq_sta_init(rwnx_hw, sta, txq_status); + rwnx_txq_tdls_vif_init(rwnx_vif); + rwnx_mu_group_sta_init(sta, NULL); + /* Look for TDLS Channel Switch Prohibited flag in the Extended + * Capability Information Element + */ + extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie, + ind->assoc_rsp_ie_len); + if (extcap_ie && extcap_ie[1] >= 5) { + extcap = (void *)(extcap_ie); + rwnx_vif->tdls_chsw_prohibited = + extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED; + } + + if (rwnx_vif->wep_enabled) + rwnx_vif->wep_auth_err = false; + +#ifdef CONFIG_RWNX_BFMER + /* If Beamformer feature is activated, check if features can be used + * with the new peer device + */ + if (rwnx_hw->mod_params->bfmer) { + const u8 *vht_capa_ie; + const struct ieee80211_vht_cap *vht_cap; + + do { + /* Look for VHT Capability Information Element */ + vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie, + ind->assoc_rsp_ie_len); + + /* Stop here if peer device does not support VHT */ + if (!vht_capa_ie) + break; + + vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2); + + /* Send MM_BFMER_ENABLE_REQ message if needed */ + rwnx_send_bfmer_enable(rwnx_hw, sta, vht_cap); + } while (0); + } +#endif //(CONFIG_RWNX_BFMER) + + ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, rsp_ie, + ind->assoc_rsp_ie_len); + if (ht_ie) + rwnx_vif->sta.ap->ht = true; + vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie, + ind->assoc_rsp_ie_len); + if (vht_ie) + rwnx_vif->sta.ap->vht = true; + +#ifdef CONFIG_RWNX_MON_DATA + // If there are 1 sta and 1 monitor interface active at the same time + // then monitor interface channel context is always the same as the STA + // interface. This doesn't work with 2 STA interfaces but we don't want + // to support it. + if (rwnx_hw->monitor_vif != RWNX_INVALID_VIF) { + struct rwnx_vif *rwnx_mon_vif = + rwnx_hw->vif_table[rwnx_hw->monitor_vif]; + rwnx_chanctx_unlink(rwnx_mon_vif); + rwnx_chanctx_link(rwnx_mon_vif, ind->ch_idx, NULL); + } +#endif + // atomic_set(&rwnx_vif->drv_conn_state, + // (int)RWNX_DRV_STATUS_CONNECTED); + } else if (ind->status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + if (rwnx_vif->wep_enabled) { + rwnx_vif->wep_auth_err = true; + AICWFDBG(LOGINFO, "con ind wep_auth_err %d\n", + rwnx_vif->wep_auth_err); + } + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED); + } else { + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED); + } + + AICWFDBG(LOGINFO, + "%s ind->roamed:%d ind->status_code:%d rwnx_vif->drv_conn_state:%d\r\n", + __func__, ind->roamed, ind->status_code, + (int)atomic_read(&rwnx_vif->drv_conn_state)); + + if (ind->status_code == 0 && + (int)atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_DISCONNECTING) { + AICWFDBG(LOGINFO, "%s the disconnection has been requested, return it\r\n", + __func__); + goto exit; + } + + if (!ind->roamed) { // not roaming + cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, + req_ie, ind->assoc_req_ie_len, + rsp_ie, ind->assoc_rsp_ie_len, ind->status_code, + GFP_ATOMIC); + if (ind->status_code == 0) { + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, + (int)RWNX_DRV_STATUS_CONNECTED); + AICWFDBG(LOGINFO, + "%s cfg80211_connect_result pass, rwnx_vif->drv_conn_state:%d\r\n", + __func__, (int)atomic_read(&rwnx_vif->drv_conn_state)); + rwnx_vif->sta.link_time = jiffies; + } else { + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, + (int)RWNX_DRV_STATUS_DISCONNECTED); + rwnx_external_auth_disable(rwnx_vif); + } + } else { // roaming + if (ind->status_code != 0) { + AICWFDBG(LOGINFO, "%s roaming fail to notify disconnect \r\n", + __func__); + cfg80211_disconnected(dev, 0, NULL, 0, 1, GFP_ATOMIC); + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, + (int)RWNX_DRV_STATUS_DISCONNECTED); + rwnx_external_auth_disable(rwnx_vif); + } else { + struct cfg80211_roam_info info; + + memset(&info, 0, sizeof(info)); + if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT) + info.links[0].channel = + rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan; + info.links[0].bssid = (const u8 *)ind->bssid.array; + info.req_ie = req_ie; + info.req_ie_len = ind->assoc_req_ie_len; + info.resp_ie = rsp_ie; + info.resp_ie_len = ind->assoc_rsp_ie_len; + cfg80211_roamed(dev, &info, GFP_ATOMIC); + rwnx_vif->sta.link_time = jiffies; + + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, + (int)RWNX_DRV_STATUS_CONNECTED); + rwnx_vif->sta.link_time = jiffies; + } + rwnx_vif->sta.is_roam = false; + } + + if (ind->status_code == 0) { + netif_tx_start_all_queues(dev); + netif_carrier_on(dev); + } + +exit: + rwnx_vif->sta.is_roam = false; + return 0; +} + +void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif) +{ + struct wiphy *wiphy = rwnx_hw->wiphy; + struct cfg80211_bss *bss = NULL; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + bss = cfg80211_get_bss(wiphy, NULL /*notify_channel */, rwnx_vif->sta.bssid, + rwnx_vif->sta.ssid, rwnx_vif->sta.ssid_len, + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY(true)); // temp set true + if (bss) { + cfg80211_unlink_bss(wiphy, bss); + AICWFDBG(LOGINFO, "%s(): cfg80211_unlink %s!!\n", __func__, + rwnx_vif->sta.ssid); + cfg80211_put_bss(wiphy, bss); + } else { + AICWFDBG(LOGINFO, "%s(): cfg80211_unlink error %s!!\n", __func__, + rwnx_vif->sta.ssid); + } +} + +static inline int rwnx_rx_sm_disconnect_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct net_device *dev; +#ifdef AICWF_RX_REORDER + struct reord_ctrl_info *reord_info, *tmp; + u8 *macaddr; + struct aicwf_rx_priv *rx_priv; +#endif + + AICWFDBG(LOGINFO, "%s reason code:%d \r\n", __func__, ind->reason_code); + + if (!rwnx_vif) + return 0; + + if ((int)atomic_read(&rwnx_vif->drv_conn_state) == + (int)RWNX_DRV_STATUS_DISCONNECTED) { + AICWFDBG(LOGINFO, "%s, is already disconnected, drop disconnect ind", + __func__); + return 0; + } + + dev = rwnx_vif->ndev; + + if (!rwnx_vif->sta.is_roam) + rwnx_cfg80211_unlink_bss(rwnx_hw, rwnx_vif); + else + AICWFDBG(LOGINFO, "%s roaming no rwnx_cfg80211_unlink_bss \r\n", + __func__); + if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) + rwnx_hw->is_p2p_connected = 0; + /* if vif is not up, rwnx_close has already been called */ + if (rwnx_vif->up) { + if (!ind->ft_over_ds && !ind->reassoc) { + cfg80211_disconnected(dev, ind->reason_code, NULL, 0, + (ind->reason_code < 1), GFP_ATOMIC); + } + netif_tx_stop_all_queues(dev); + netif_carrier_off(dev); + } + +#ifdef CONFIG_RWNX_BFMER + /* Disable Beamformer if supported */ + rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.ap); +#endif //(CONFIG_RWNX_BFMER) + +#ifdef AICWF_RX_REORDER +#ifdef AICWF_SDIO_SUPPORT + rx_priv = rwnx_hw->sdiodev->rx_priv; +#else + rx_priv = rwnx_hw->usbdev->rx_priv; +#endif + if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION || + rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { + macaddr = (u8 *)rwnx_vif->ndev->dev_addr; + AICWFDBG(LOGDEBUG, "deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1], + macaddr[2], macaddr[3], macaddr[4], macaddr[5]); + + // spin_lock_bh(&rx_priv->stas_reord_lock); + list_for_each_entry_safe(reord_info, tmp, &rx_priv->stas_reord_list, + list) { + macaddr = (u8 *)rwnx_vif->ndev->dev_addr; + AICWFDBG(LOGDEBUG, "reord_mac:%x,%x,%x,%x,%x,%x\r\n", + reord_info->mac_addr[0], reord_info->mac_addr[1], + reord_info->mac_addr[2], reord_info->mac_addr[3], + reord_info->mac_addr[4], reord_info->mac_addr[5]); + if (!memcmp(reord_info->mac_addr, macaddr, 6)) { + reord_deinit_sta(rx_priv, reord_info); + break; + } + } + // spin_unlock_bh(&rx_priv->stas_reord_lock); + } else if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || + rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { + // WARN_ON_ONCE(); // should be not here: del_sta function + } +#endif + + rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.ap); + rwnx_txq_tdls_vif_deinit(rwnx_vif); + rwnx_vif->sta.ap->valid = false; + rwnx_vif->sta.ap = NULL; + rwnx_external_auth_disable(rwnx_vif); + rwnx_chanctx_unlink(rwnx_vif); + + // msleep(200); + if (!rwnx_vif->sta.is_roam) + rwnx_set_conn_state(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED); + + return 0; +} + +static inline int rwnx_rx_sm_external_auth_required_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct sm_external_auth_required_ind *ind = + (struct sm_external_auth_required_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct net_device *dev = rwnx_vif->ndev; + struct cfg80211_external_auth_params params; + int ret = 0; + struct wireless_dev *wdev = dev->ieee80211_ptr; + int retry_counter = 10; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + params.action = NL80211_EXTERNAL_AUTH_START; + memcpy(params.bssid, ind->bssid.array, ETH_ALEN); + params.ssid.ssid_len = ind->ssid.length; + memcpy(params.ssid.ssid, ind->ssid.array, + min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid))); + params.key_mgmt_suite = ind->akm; + + while (wdev->conn_owner_nlportid == 0) { + AICWFDBG(LOGINFO, + "%s WARNING conn_owner_nlportid = 0, msleep 100ms.\r\n", + __func__); + msleep(100); + retry_counter--; + if (retry_counter == 0) + break; + } + AICWFDBG(LOGINFO, "%s wdev->conn_owner_nlportid:%d \r\n", __func__, + (int)wdev->conn_owner_nlportid); + ret = cfg80211_external_auth_request(dev, ¶ms, GFP_ATOMIC); + if (ind->vif_idx > NX_VIRT_DEV_MAX || !rwnx_vif->up || + RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION || + ret) { + wiphy_err(rwnx_hw->wiphy, + "Failed to start external auth on vif %d, rwnx_vif->up %d, iftype:%d, ret %d", + ind->vif_idx, rwnx_vif->up, RWNX_VIF_TYPE(rwnx_vif), ret); + rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif, + WLAN_STATUS_UNSPECIFIED_FAILURE); + return 0; + } + + rwnx_external_auth_enable(rwnx_vif); + return 0; +} + +static inline int rwnx_rx_mesh_path_create_cfm(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mesh_path_create_cfm *cfm = + (struct mesh_path_create_cfm *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[cfm->vif_idx]; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Check we well have a Mesh Point Interface */ + if (rwnx_vif && RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) + rwnx_vif->ap.create_path = false; + + return 0; +} + +static inline int rwnx_rx_mesh_peer_update_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mesh_peer_update_ind *ind = + (struct mesh_peer_update_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[ind->sta_idx]; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX) || + (rwnx_vif && RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT) || + ind->sta_idx >= NX_REMOTE_STA_MAX) + return 1; + + /* Check we well have a Mesh Point Interface */ + if (!rwnx_vif->user_mpm) { + /* Check if peer link has been established or lost */ + if (ind->estab) { + if (!rwnx_sta->valid) { + u8 txq_status; + + rwnx_sta->valid = true; + rwnx_sta->sta_idx = ind->sta_idx; + rwnx_sta->ch_idx = rwnx_vif->ch_index; + rwnx_sta->vif_idx = ind->vif_idx; + rwnx_sta->vlan_idx = rwnx_sta->vif_idx; + rwnx_sta->ps.active = false; + rwnx_sta->qos = true; + rwnx_sta->aid = ind->sta_idx + 1; + // rwnx_sta->acm = ind->acm; + memcpy(rwnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN); + + rwnx_chanctx_link(rwnx_vif, rwnx_sta->ch_idx, NULL); + + /* Add the station in the list of VIF's stations */ + INIT_LIST_HEAD(&rwnx_sta->list); + list_add_tail(&rwnx_sta->list, &rwnx_vif->ap.sta_list); + + /* Initialize the TX queues */ + if (rwnx_sta->ch_idx == rwnx_hw->cur_chanctx) + txq_status = 0; + else + txq_status = RWNX_TXQ_STOP_CHAN; + + rwnx_txq_sta_init(rwnx_hw, rwnx_sta, txq_status); +#ifdef CONFIG_RWNX_BFMER + // TODO: update indication to contains vht capabilities + if (rwnx_hw->mod_params->bfmer) + rwnx_send_bfmer_enable(rwnx_hw, rwnx_sta, NULL); + + rwnx_mu_group_sta_init(rwnx_sta, NULL); +#endif /* CONFIG_RWNX_BFMER */ + + } else { + WARN_ON(0); + } + } else { + if (rwnx_sta->valid) { + rwnx_sta->ps.active = false; + rwnx_sta->valid = false; + + /* Remove the station from the list of VIF's station */ + list_del_init(&rwnx_sta->list); + + rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta); + } else { + WARN_ON(0); + } + } + } else { + if (!ind->estab && rwnx_sta->valid) { + /* There is no way to inform upper layer for lost of peer, still + * clean everything in the driver + */ + rwnx_sta->ps.active = false; + rwnx_sta->valid = false; + + /* Remove the station from the list of VIF's station */ + list_del_init(&rwnx_sta->list); + + rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta); + } else { + WARN_ON(0); + } + } + + return 0; +} + +static inline int rwnx_rx_mesh_path_update_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mesh_path_update_ind *ind = + (struct mesh_path_update_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct rwnx_mesh_path *mesh_path; + bool found = false; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) + return 1; + + if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)) + return 0; + + /* Look for path with provided target address */ + list_for_each_entry(mesh_path, &rwnx_vif->ap.mpath_list, list) { + if (mesh_path->path_idx == ind->path_idx) { + found = true; + break; + } + } + + /* Check if element has been deleted */ + if (ind->delete) { + if (found) { +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_mesh_delete_path(mesh_path); +#endif + /* Remove element from list */ + list_del_init(&mesh_path->list); + /* Free the element */ + kfree(mesh_path); + } + } else { + if (found) { + // Update the Next Hop STA + mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx]; +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_mesh_update_path(mesh_path); +#endif + } else { + // Allocate a Mesh Path structure + mesh_path = + kmalloc_obj(struct rwnx_mesh_path, GFP_ATOMIC); + + if (mesh_path) { + INIT_LIST_HEAD(&mesh_path->list); + + mesh_path->path_idx = ind->path_idx; + mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx]; + memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr, + MAC_ADDR_LEN); + + // Insert the path in the list of path + list_add_tail(&mesh_path->list, &rwnx_vif->ap.mpath_list); +#ifdef AIC8800_CREATE_TRACE_POINTS + trace_mesh_create_path(mesh_path); +#endif + } + } + } + + return 0; +} + +static inline int rwnx_rx_mesh_proxy_update_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + struct mesh_proxy_update_ind *ind = + (struct mesh_proxy_update_ind *)msg->param; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx]; + struct rwnx_mesh_proxy *mesh_proxy; + bool found = false; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) + return 1; + + if (!rwnx_vif || RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT) + return 0; + + /* Look for path with provided external STA address */ + list_for_each_entry(mesh_proxy, &rwnx_vif->ap.proxy_list, list) { + if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) { + found = true; + break; + } + } + + if (ind->delete && found) { + /* Delete mesh path */ + list_del_init(&mesh_proxy->list); + kfree(mesh_proxy); + } else if (!ind->delete && !found) { + /* Allocate a Mesh Path structure */ + mesh_proxy = + kmalloc_obj(*mesh_proxy, GFP_ATOMIC); + + if (mesh_proxy) { + INIT_LIST_HEAD(&mesh_proxy->list); + + memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN); + mesh_proxy->local = ind->local; + + if (!ind->local) + memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr, + MAC_ADDR_LEN); + + /* Insert the path in the list of path */ + list_add_tail(&mesh_proxy->list, &rwnx_vif->ap.proxy_list); + } + } + + return 0; +} +#endif /* CONFIG_RWNX_FULLMAC */ + +/*************************************************************************** + * Messages from DEBUG task + **************************************************************************/ +static inline int rwnx_rx_dbg_error_ind(struct rwnx_hw *rwnx_hw, + struct rwnx_cmd *cmd, + struct ipc_e2a_msg *msg) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); + + return 0; +} + +#ifdef CONFIG_RWNX_FULLMAC + +static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = { + [MSG_I(MM_CHANNEL_SWITCH_IND)] = rwnx_rx_chan_switch_ind, + [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = rwnx_rx_chan_pre_switch_ind, + [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = rwnx_rx_remain_on_channel_exp_ind, + [MSG_I(MM_PS_CHANGE_IND)] = rwnx_rx_ps_change_ind, + [MSG_I(MM_TRAFFIC_REQ_IND)] = rwnx_rx_traffic_req_ind, + [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = rwnx_rx_p2p_vif_ps_change_ind, + [MSG_I(MM_CSA_COUNTER_IND)] = rwnx_rx_csa_counter_ind, + [MSG_I(MM_CSA_FINISH_IND)] = rwnx_rx_csa_finish_ind, + [MSG_I(MM_CSA_TRAFFIC_IND)] = rwnx_rx_csa_traffic_ind, + [MSG_I(MM_CHANNEL_SURVEY_IND)] = rwnx_rx_channel_survey_ind, + [MSG_I(MM_P2P_NOA_UPD_IND)] = rwnx_rx_p2p_noa_upd_ind, + [MSG_I(MM_RSSI_STATUS_IND)] = rwnx_rx_rssi_status_ind, + [MSG_I(MM_PKTLOSS_IND)] = rwnx_rx_pktloss_notify_ind, + [MSG_I(MM_APM_STALOSS_IND)] = rwnx_apm_staloss_ind, + [MSG_I(MM_ARP_ANNOUNCEMENT_IND)] = rwnx_arp_announcement_ind, +}; + +static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = { + [MSG_I(SCANU_START_CFM)] = rwnx_rx_scanu_start_cfm, + [MSG_I(SCANU_RESULT_IND)] = rwnx_rx_scanu_result_ind, +}; + +static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = { + [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = rwnx_rx_me_tkip_mic_failure_ind, + [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = rwnx_rx_me_tx_credits_update_ind, +}; + +static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = { + [MSG_I(SM_CONNECT_IND)] = rwnx_rx_sm_connect_ind, + [MSG_I(SM_DISCONNECT_IND)] = rwnx_rx_sm_disconnect_ind, + [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = + rwnx_rx_sm_external_auth_required_ind, +}; + +static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {}; + +static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = { + [MSG_I(MESH_PATH_CREATE_CFM)] = rwnx_rx_mesh_path_create_cfm, + [MSG_I(MESH_PEER_UPDATE_IND)] = rwnx_rx_mesh_peer_update_ind, + [MSG_I(MESH_PATH_UPDATE_IND)] = rwnx_rx_mesh_path_update_ind, + [MSG_I(MESH_PROXY_UPDATE_IND)] = rwnx_rx_mesh_proxy_update_ind, +}; + +#endif /* CONFIG_RWNX_FULLMAC */ + +static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = { + [MSG_I(DBG_ERROR_IND)] = rwnx_rx_dbg_error_ind, +}; + +static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = { + [MSG_I(TDLS_CHAN_SWITCH_CFM)] = rwnx_rx_tdls_chan_switch_cfm, + [MSG_I(TDLS_CHAN_SWITCH_IND)] = rwnx_rx_tdls_chan_switch_ind, + [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = rwnx_rx_tdls_chan_switch_base_ind, + [MSG_I(TDLS_PEER_PS_IND)] = rwnx_rx_tdls_peer_ps_ind, +}; + +static msg_cb_fct *msg_hdlrs[] = { + [TASK_MM] = mm_hdlrs, [TASK_DBG] = dbg_hdlrs, +#ifdef CONFIG_RWNX_FULLMAC + [TASK_TDLS] = tdls_hdlrs, [TASK_SCANU] = scan_hdlrs, + [TASK_ME] = me_hdlrs, [TASK_SM] = sm_hdlrs, + [TASK_APM] = apm_hdlrs, [TASK_MESH] = mesh_hdlrs, +#endif /* CONFIG_RWNX_FULLMAC */ +}; + +/* + * + */ +void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg) +{ + // AICWFDBG(LOGDEBUG, "%s msg->id:0x%x \r\n", __func__, msg->id); + + rwnx_hw->cmd_mgr->msgind(rwnx_hw->cmd_mgr, msg, + msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]); +} + +void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len) +{ + (void)len; + + if (!rwnx_hw || !rwnx_hw->fwlog_en) { + pr_err("FWLOG-OVFL: %s", msg); + return; + } + + AICWFDBG(LOGDEBUG, "FWLOG: %s", msg); +} diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h new file mode 100644 index 0000000000000..211f2e616cbf4 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_rx.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RWNX_MSG_RX_H_ +#define _RWNX_MSG_RX_H_ + +void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg); +void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len); + +#endif /* _RWNX_MSG_RX_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c new file mode 100644 index 0000000000000..08dd58442b8a7 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.c @@ -0,0 +1,3619 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @brief TX function definitions + * + ****************************************************************************** + */ + +#include "rwnx_msg_tx.h" +#include "reg_access.h" +#include "rwnx_mod_params.h" +#ifdef CONFIG_RWNX_BFMER +#include "rwnx_bfmer.h" +#endif //(CONFIG_RWNX_BFMER) +#include "aicwf_txrxif.h" +#include "rwnx_cmds.h" +#include "rwnx_compat.h" +#include "rwnx_main.h" +#include "rwnx_strs.h" +#include +#include "aicwf_sdio.h" +#include "aicwf_chip_ops.h" + +static const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}}; + +/* Default MAC Rx filters that can be changed by mac80211 + * (via the configure_filter() callback) + */ +#define RWNX_MAC80211_CHANGEABLE \ + (NXMAC_ACCEPT_BA_BIT | NXMAC_ACCEPT_BAR_BIT | \ + NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT | NXMAC_ACCEPT_PROBE_REQ_BIT | \ + NXMAC_ACCEPT_PS_POLL_BIT) + +/* Default MAC Rx filters that cannot be changed by mac80211 */ +#define RWNX_MAC80211_NOT_CHANGEABLE \ + (NXMAC_ACCEPT_QO_S_NULL_BIT | NXMAC_ACCEPT_Q_DATA_BIT | \ + NXMAC_ACCEPT_DATA_BIT | NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT | \ + NXMAC_ACCEPT_MY_UNICAST_BIT | NXMAC_ACCEPT_BROADCAST_BIT | \ + NXMAC_ACCEPT_BEACON_BIT | NXMAC_ACCEPT_PROBE_RESP_BIT) + +/* Default MAC Rx filter */ +#define RWNX_DEFAULT_RX_FILTER \ + (RWNX_MAC80211_CHANGEABLE | RWNX_MAC80211_NOT_CHANGEABLE) + +const int bw2chnl[] = { + [NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20, + [NL80211_CHAN_WIDTH_20] = PHY_CHNL_BW_20, + [NL80211_CHAN_WIDTH_40] = PHY_CHNL_BW_40, + [NL80211_CHAN_WIDTH_80] = PHY_CHNL_BW_80, + [NL80211_CHAN_WIDTH_160] = PHY_CHNL_BW_160, + [NL80211_CHAN_WIDTH_80P80] = PHY_CHNL_BW_80P80, +}; + +const int chnl2bw[] = { + [PHY_CHNL_BW_20] = NL80211_CHAN_WIDTH_20, + [PHY_CHNL_BW_40] = NL80211_CHAN_WIDTH_40, + [PHY_CHNL_BW_80] = NL80211_CHAN_WIDTH_80, + [PHY_CHNL_BW_160] = NL80211_CHAN_WIDTH_160, + [PHY_CHNL_BW_80P80] = NL80211_CHAN_WIDTH_80P80, +}; + +#define RWNX_CMD_ARRAY_SIZE 20 +#define RWNX_CMD_HIGH_WATER_SIZE (RWNX_CMD_ARRAY_SIZE / 2) + +static struct rwnx_cmd cmd_array[RWNX_CMD_ARRAY_SIZE]; +static spinlock_t cmd_array_lock; +static int cmd_array_index; + +/*****************************************************************************/ +/* + * Parse the ampdu density to retrieve the value in usec, according to the + * values defined in ieee80211.h + */ +static inline u8 rwnx_ampdudensity2usec(u8 ampdudensity) +{ + switch (ampdudensity) { + case IEEE80211_HT_MPDU_DENSITY_NONE: + return 0; + /* 1 microsecond is our granularity */ + case IEEE80211_HT_MPDU_DENSITY_0_25: + case IEEE80211_HT_MPDU_DENSITY_0_5: + case IEEE80211_HT_MPDU_DENSITY_1: + return 1; + case IEEE80211_HT_MPDU_DENSITY_2: + return 2; + case IEEE80211_HT_MPDU_DENSITY_4: + return 4; + case IEEE80211_HT_MPDU_DENSITY_8: + return 8; + case IEEE80211_HT_MPDU_DENSITY_16: + return 16; + default: + return 0; + } +} + +static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto) +{ + if (crypto->cipher_group == WLAN_CIPHER_SUITE_WEP40 || + crypto->cipher_group == WLAN_CIPHER_SUITE_WEP104) + return false; + + return true; +} + +static inline bool is_non_blocking_msg(int id) +{ + return (id == MM_TIM_UPDATE_REQ || id == ME_RC_SET_RATE_REQ || + id == MM_BFMER_ENABLE_REQ || id == ME_TRAFFIC_IND_REQ || + id == TDLS_PEER_TRAFFIC_IND_REQ || id == MESH_PATH_CREATE_REQ || + id == MESH_PROXY_ADD_REQ || id == SM_EXTERNAL_AUTH_REQUIRED_RSP); +} + +static inline u8 get_chan_flags(uint32_t flags) +{ + u8 chan_flags = 0; +#ifdef AIC8800_RADAR_OR_IR_DETECT + + if (flags & IEEE80211_CHAN_NO_IR) + chan_flags |= CHAN_NO_IR; + + if (flags & IEEE80211_CHAN_RADAR) + chan_flags |= CHAN_RADAR; +#endif + return chan_flags; +} + +static inline s8 chan_to_fw_pwr(int power) +{ + return power > 127 ? 127 : (s8)power; +} + +static inline void limit_chan_bw(u8 *bw, u16 primary, u16 *center1) +{ + int oft, new_oft = 10; + + if (*bw <= PHY_CHNL_BW_40) + return; + + oft = *center1 - primary; + *bw = PHY_CHNL_BW_40; + + if (oft < 0) + new_oft = new_oft * -1; + if (abs(oft) == 10 || abs(oft) == 50) + new_oft = new_oft * -1; + + *center1 = primary + new_oft; +} + +struct rwnx_cmd *rwnx_cmd_malloc(void) +{ + struct rwnx_cmd *cmd = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&cmd_array_lock, flags); + + for (cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE; + cmd_array_index++) { + if (cmd_array[cmd_array_index].used == 0) { + AICWFDBG(LOGTRACE, "%s get cmd_array[%d]:%p \r\n", __func__, + cmd_array_index, &cmd_array[cmd_array_index]); + cmd = &cmd_array[cmd_array_index]; + cmd_array[cmd_array_index].used = 1; + break; + } + } + + if (cmd_array_index >= RWNX_CMD_HIGH_WATER_SIZE) { + AICWFDBG(LOGERROR, "%s cmd(%d) was pending...\r\n", __func__, + cmd_array_index); + mdelay(100); + } + + if (!cmd) + AICWFDBG(LOGERROR, "%s array is empty...\r\n", __func__); + + spin_unlock_irqrestore(&cmd_array_lock, flags); + + return cmd; +} + +void rwnx_cmd_free(struct rwnx_cmd *cmd) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&cmd_array_lock, flags); + cmd->used = 0; + AICWFDBG(LOGTRACE, "%s cmd_array[%d]:%p \r\n", __func__, cmd->array_id, + cmd); + spin_unlock_irqrestore(&cmd_array_lock, flags); +} + +int rwnx_init_cmd_array(void) +{ + AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__); + spin_lock_init(&cmd_array_lock); + + for (cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE; + cmd_array_index++) { + AICWFDBG(LOGTRACE, "%s cmd_queue[%d]:%p \r\n", __func__, + cmd_array_index, &cmd_array[cmd_array_index]); + cmd_array[cmd_array_index].used = 0; + cmd_array[cmd_array_index].array_id = cmd_array_index; + } + AICWFDBG(LOGTRACE, "%s Exit \r\n", __func__); + + return 0; +} + +void rwnx_free_cmd_array(void) +{ + AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__); + + for (cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE; + cmd_array_index++) + cmd_array[cmd_array_index].used = 0; + + AICWFDBG(LOGTRACE, "%s Exit \r\n", __func__); +} + +/* + ****************************************************************************** + * @brief Allocate memory for a message + * + * This primitive allocates memory for a message that has to be sent. The memory + * is allocated dynamically on the heap and the length of the variable parameter + * structure has to be provided in order to allocate the correct size. + * + * Several additional parameters are provided which will be preset in the + *message and which may be used internally to choose the kind of memory to + *allocate. + * + * The memory allocated will be automatically freed by the kernel, after the + * pointer has been sent to ke_msg_send(). If the message is not sent, it must + * be freed explicitly with ke_msg_free(). + * + * Allocation failure is considered critical and should not happen. + * + * @param[in] id Message identifier + * @param[in] dest_id Destination Task Identifier + * @param[in] src_id Source Task Identifier + * @param[in] param_len Size of the message parameters to be allocated + * + * @return Pointer to the parameter member of the ke_msg. If the parameter + * structure is empty, the pointer will point to the end of the message + * and should not be used (except to retrieve the message pointer or to + * send the message) + ****************************************************************************** + */ +static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id, + lmac_task_id_t const dest_id, + lmac_task_id_t const src_id, + uint16_t const param_len) +{ + struct lmac_msg *msg; + gfp_t flags; + + // if (is_non_blocking_msg(id) && (in_softirq()||in_atomic())) + flags = GFP_ATOMIC; + // else + // flags = GFP_KERNEL; + + // msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len, flags); + msg = kzalloc(sizeof(*msg) + param_len, flags); + if (!msg) { + AICWFDBG(LOGERROR, "%s: msg allocation failed\n", __func__); + return NULL; + } + msg->id = id; + msg->dest_id = dest_id; + msg->src_id = src_id; + msg->param_len = param_len; + // AICWFDBG(LOGDEBUG, "rwnx_msg_zalloc size=%d id=%d\n",msg->param_len,msg->id); + + return msg->param; +} + +static void rwnx_msg_free(struct rwnx_hw *rwnx_hw, const void *msg_params) +{ + struct lmac_msg *msg = container_of((void *)msg_params, struct lmac_msg, param); + + RWNX_DBG(RWNX_FN_ENTRY_STR); + /* Free the message */ + kfree(msg); +} + +// void rwnx_pm_relax(struct aic_sdio_dev *sdiodev); +// void rwnx_pm_stay_awake(struct aic_sdio_dev *sdiodev); + +static int rwnx_send_msg(struct rwnx_hw *rwnx_hw, const void *msg_params, + int reqcfm, lmac_msg_id_t reqid, void *cfm) +{ + struct lmac_msg *msg; + struct rwnx_cmd *cmd; + bool nonblock; + int ret = 0; + u8 empty = 0; + + // RWNX_DBG(RWNX_FN_ENTRY_STR); + AICWFDBG(LOGTRACE, + "%s (%d)%s reqcfm:%d in_hardirq:%d in_softirq:%d irqs_disabled:%d\r\n", + __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_hardirq(), + (int)in_softirq(), (int)irqs_disabled()); + +#ifdef AICWF_SDIO_SUPPORT + if (!aicwf_sdio_activity_get(rwnx_hw, rwnx_hw->ws_tx)) { + rwnx_msg_free(rwnx_hw, msg_params); + return -EBUSY; + } + if (rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) { + rwnx_msg_free(rwnx_hw, msg_params); + sdio_err("bus is down\n"); + aicwf_sdio_activity_put(rwnx_hw, rwnx_hw->ws_tx); + return 0; + } +#endif + + msg = container_of((void *)msg_params, struct lmac_msg, param); + + // nonblock = is_non_blocking_msg(msg->id); + nonblock = 0; + cmd = rwnx_cmd_malloc(); // kzalloc_obj(struct rwnx_cmd, nonblock ? + // GFP_ATOMIC : GFP_KERNEL); + cmd->result = -EINTR; + cmd->id = msg->id; + cmd->reqid = reqid; + cmd->a2e_msg = msg; + cmd->e2a_msg = cfm; + if (nonblock) + cmd->flags = RWNX_CMD_FLAG_NONBLOCK; + if (reqcfm) + cmd->flags |= RWNX_CMD_FLAG_REQ_CFM; + + if (cfm) { + do { + if (rwnx_hw->cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) + break; + spin_lock_bh(&rwnx_hw->cmd_mgr->lock); + empty = list_empty(&rwnx_hw->cmd_mgr->cmds); + spin_unlock_bh(&rwnx_hw->cmd_mgr->lock); + if (!empty) { + if (in_softirq()) { + AICWFDBG(LOGDEBUG, + "AICWF_LOG in_softirq:check cmdqueue empty\n"); + mdelay(10); + } else { + AICWFDBG(LOGDEBUG, "AICWF_LOG check cmdqueue empty\n"); + msleep(50); + } + } + } while (!empty); // wait for cmd queue empty + } + + if (reqcfm) { + cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more + ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd); + } else { +#ifdef AICWF_SDIO_SUPPORT + aicwf_set_cmd_tx((void *)(rwnx_hw->sdiodev), cmd->a2e_msg, + sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); +#else + aicwf_set_cmd_tx((void *)(rwnx_hw->usbdev), cmd->a2e_msg, + sizeof(struct lmac_msg) + cmd->a2e_msg->param_len); +#endif + } + + if (!reqcfm || ret) + rwnx_cmd_free(cmd); // kfree(cmd); + + aicwf_sdio_activity_put(rwnx_hw, rwnx_hw->ws_tx); + return 0; +} + +static int rwnx_send_msg1(struct rwnx_hw *rwnx_hw, const void *msg_params, + int reqcfm, lmac_msg_id_t reqid, void *cfm, + bool defer) +{ + struct lmac_msg *msg; + struct rwnx_cmd *cmd; + bool nonblock; + int ret = 0; + + // RWNX_DBG(RWNX_FN_ENTRY_STR); + AICWFDBG(LOGDEBUG, + "AICWF_LOG %s (%d)%s reqcfm:%d in_hardirq:%d in_softirq:%d irqs_disabled:%d\r\n", + __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_hardirq(), + (int)in_softirq(), (int)irqs_disabled()); + + if (!aicwf_sdio_activity_get(rwnx_hw, rwnx_hw->ws_tx)) { + rwnx_msg_free(rwnx_hw, msg_params); + return -EBUSY; + } + msg = container_of((void *)msg_params, struct lmac_msg, param); + + // nonblock = is_non_blocking_msg(msg->id); + nonblock = 0; + cmd = rwnx_cmd_malloc(); // kzalloc_obj(struct rwnx_cmd, nonblock ? + // GFP_ATOMIC : GFP_KERNEL); + cmd->result = -EINTR; + cmd->id = msg->id; + cmd->reqid = reqid; + cmd->a2e_msg = msg; + cmd->e2a_msg = cfm; + if (nonblock) + cmd->flags = RWNX_CMD_FLAG_NONBLOCK; + if (reqcfm) + cmd->flags |= RWNX_CMD_FLAG_REQ_CFM; + + if (reqcfm) { + cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more + if (!defer) + ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd); + else + ret = cmd_mgr_queue_force_defer(rwnx_hw->cmd_mgr, cmd); + } + + if (!reqcfm || ret) + rwnx_cmd_free(cmd); // kfree(cmd); + + if (!ret) + ret = cmd->result; + + aicwf_sdio_activity_put(rwnx_hw, rwnx_hw->ws_tx); + // return ret; + return 0; +} + +/****************************************************************************** + * Control messages handling functions (FULLMAC) + *****************************************************************************/ +int rwnx_send_reset(struct rwnx_hw *rwnx_hw) +{ + void *void_param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* RESET REQ has no parameter */ + void_param = rwnx_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0); + if (!void_param) + return -ENOMEM; + + return rwnx_send_msg(rwnx_hw, void_param, 1, MM_RESET_CFM, NULL); +} + +int rwnx_send_start(struct rwnx_hw *rwnx_hw) +{ + struct mm_start_req *start_req_param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the START REQ message */ + start_req_param = rwnx_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_start_req)); + if (!start_req_param) + return -ENOMEM; + + /* Set parameters for the START message */ + memcpy(&start_req_param->phy_cfg, &rwnx_hw->phy.cfg, + sizeof(rwnx_hw->phy.cfg)); + start_req_param->uapsd_timeout = (u32)rwnx_hw->mod_params->uapsd_timeout; + start_req_param->lp_clk_accuracy = (u16)rwnx_hw->mod_params->lp_clk_ppm; + + /* Send the START REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, start_req_param, 1, MM_START_CFM, NULL); +} + +int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm) +{ + void *void_param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* VERSION REQ has no parameter */ + void_param = rwnx_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0); + if (!void_param) + return -ENOMEM; + + return rwnx_send_msg(rwnx_hw, void_param, 1, MM_VERSION_CFM, cfm); +} + +int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac, + enum nl80211_iftype iftype, bool p2p, + struct mm_add_if_cfm *cfm) +{ + struct mm_add_if_req *add_if_req_param; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ADD_IF_REQ message */ + add_if_req_param = rwnx_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_add_if_req)); + if (!add_if_req_param) + return -ENOMEM; + + /* Set parameters for the ADD_IF_REQ message */ + memcpy(&add_if_req_param->addr.array[0], mac, ETH_ALEN); + switch (iftype) { +#if !IS_ENABLED(CONFIG_AIC8800_STA_ONLY) +#ifdef CONFIG_RWNX_FULLMAC + // case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_P2P_CLIENT: + add_if_req_param->p2p = true; + fallthrough; + // no break +#endif /* CONFIG_RWNX_FULLMAC */ +#endif + case NL80211_IFTYPE_STATION: + add_if_req_param->type = MM_STA; + break; + +#if !IS_ENABLED(CONFIG_AIC8800_STA_ONLY) + case NL80211_IFTYPE_ADHOC: + add_if_req_param->type = MM_IBSS; + break; + +#ifdef CONFIG_RWNX_FULLMAC + case NL80211_IFTYPE_P2P_GO: + add_if_req_param->p2p = true; + fallthrough; + // no break +#endif /* CONFIG_RWNX_FULLMAC */ + case NL80211_IFTYPE_AP: + add_if_req_param->type = MM_AP; + break; + case NL80211_IFTYPE_MESH_POINT: + add_if_req_param->type = MM_MESH_POINT; + break; + case NL80211_IFTYPE_AP_VLAN: + return -1; + case NL80211_IFTYPE_MONITOR: + add_if_req_param->type = MM_MONITOR; + break; +#endif + default: + rwnx_msg_free(rwnx_hw, add_if_req_param); + return -EOPNOTSUPP; + } + + /* Send the ADD_IF_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm); +} + +int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer) +{ + struct mm_remove_if_req *remove_if_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_REMOVE_IF_REQ message */ + remove_if_req = rwnx_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_remove_if_req)); + if (!remove_if_req) + return -ENOMEM; + + /* Set parameters for the MM_REMOVE_IF_REQ message */ + remove_if_req->inst_nbr = vif_index; + + /* Send the MM_REMOVE_IF_REQ message to LMAC FW */ + return rwnx_send_msg1(rwnx_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL, + defer); +} + +int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx, + struct mm_set_channel_cfm *cfm) +{ + struct mm_set_channel_req *req; + enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT; + u16 center_freq = 2412, center_freq1 = 2412, center_freq2 = 2412; + s8 tx_power = 0; + u8 flags = 0; + enum nl80211_band band = NL80211_BAND_2GHZ; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (phy_idx >= rwnx_hw->phy.cnt) + return -EOPNOTSUPP; + + req = rwnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_channel_req)); + if (!req) + return -ENOMEM; + + if (phy_idx == 0) { +#ifdef CONFIG_RWNX_FULLMAC + /* On FULLMAC only setting channel of secondary chain */ + wiphy_err(rwnx_hw->wiphy, "Trying to set channel of primary chain"); + return 0; +#endif /* CONFIG_RWNX_FULLMAC */ + } else { + struct rwnx_sec_phy_chan *chan = &rwnx_hw->phy.sec_chan; + + width = chnl2bw[chan->type]; + band = chan->band; + center_freq = chan->prim20_freq; + center_freq1 = chan->center_freq1; + center_freq2 = chan->center_freq2; + flags = 0; + } + + req->chan.band = band; + req->chan.type = bw2chnl[width]; + req->chan.prim20_freq = center_freq; + req->chan.center1_freq = center_freq1; + req->chan.center2_freq = center_freq2; + req->chan.tx_power = tx_power; + req->chan.flags = flags; + req->index = phy_idx; + + if (rwnx_hw->phy.limit_bw) + limit_chan_bw(&req->chan.type, req->chan.prim20_freq, + &req->chan.center1_freq); + + RWNX_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n" + " hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n", + center_freq, center_freq1, center_freq2, width, band, phy_idx, + req->chan.prim20_freq, req->chan.center1_freq, + req->chan.center2_freq, req->chan.type, req->chan.band); + + /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm); +} + +int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx, + bool pairwise, u8 *key, u8 key_len, u8 key_idx, + u8 cipher_suite, struct mm_key_add_cfm *cfm) +{ + struct mm_key_add_req *key_add_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_KEY_ADD_REQ message */ + key_add_req = rwnx_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_key_add_req)); + if (!key_add_req) + return -ENOMEM; + + /* Set parameters for the MM_KEY_ADD_REQ message */ + if (sta_idx != 0xFF) { + /* Pairwise key */ + key_add_req->sta_idx = sta_idx; + } else { + /* Default key */ + key_add_req->sta_idx = sta_idx; + key_add_req->key_idx = (u8)key_idx; /* only useful for default keys */ + } + key_add_req->pairwise = pairwise; + key_add_req->inst_nbr = vif_idx; + key_add_req->key.length = key_len; + memcpy(&key_add_req->key.array[0], key, key_len); + + key_add_req->cipher_suite = cipher_suite; + + RWNX_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", + __func__, key_add_req->sta_idx, key_add_req->key_idx, + key_add_req->inst_nbr, key_add_req->cipher_suite, + key_add_req->key.length); +#if defined(CONFIG_RWNX_DBG) || defined(CONFIG_DYNAMIC_DEBUG) + print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, + key_add_req->key.length); +#endif + + /* Send the MM_KEY_ADD_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm); +} + +int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx) +{ + struct mm_key_del_req *key_del_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_KEY_DEL_REQ message */ + key_del_req = rwnx_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_key_del_req)); + if (!key_del_req) + return -ENOMEM; + + /* Set parameters for the MM_KEY_DEL_REQ message */ + key_del_req->hw_key_idx = hw_key_idx; + + /* Send the MM_KEY_DEL_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL); +} + +int rwnx_send_bcn(struct rwnx_hw *rwnx_hw, u8 *buf, u8 vif_idx, u16 bcn_len) +{ + struct apm_set_bcn_ie_req *bcn_ie_req; + + bcn_ie_req = rwnx_msg_zalloc(APM_SET_BEACON_IE_REQ, TASK_APM, DRV_TASK_ID, + sizeof(struct apm_set_bcn_ie_req)); + if (!bcn_ie_req) + return -ENOMEM; + + bcn_ie_req->vif_idx = vif_idx; + bcn_ie_req->bcn_ie_len = bcn_len; + memcpy(bcn_ie_req->bcn_ie, (u8 *)buf, bcn_len); + kfree(buf); + + return rwnx_send_msg(rwnx_hw, bcn_ie_req, 1, APM_SET_BEACON_IE_CFM, NULL); +} + +int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr, + u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft) +{ + struct mm_bcn_change_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_BCN_CHANGE_REQ message */ + req = rwnx_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_bcn_change_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_BCN_CHANGE_REQ message */ + req->bcn_ptr = bcn_addr; + req->bcn_len = bcn_len; + req->tim_oft = tim_oft; + req->tim_len = tim_len; + req->inst_nbr = vif_idx; + + if (csa_oft) { + int i; + + for (i = 0; i < BCN_MAX_CSA_CPT; i++) + req->csa_oft[i] = csa_oft[i]; + } + + /* Send the MM_BCN_CHANGE_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_BCN_CHANGE_CFM, NULL); +} + +int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct ieee80211_channel *chan, unsigned int duration, + struct mm_remain_on_channel_cfm *roc_cfm) +{ + struct mm_remain_on_channel_req *req; + struct cfg80211_chan_def chandef; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Create channel definition structure */ + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + + /* Build the MM_REMAIN_ON_CHANNEL_REQ message */ + req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_remain_on_channel_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */ + req->op_code = MM_ROC_OP_START; + req->vif_index = vif->vif_index; + req->duration_ms = duration; + req->band = chan->band; + req->type = bw2chnl[chandef.width]; + req->prim20_freq = chan->center_freq; + req->center1_freq = chandef.center_freq1; + req->center2_freq = chandef.center_freq2; + req->tx_power = chan_to_fw_pwr(chan->max_power); + + /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, roc_cfm); +} + +int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw) +{ + struct mm_remain_on_channel_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_REMAIN_ON_CHANNEL_REQ message */ + req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_remain_on_channel_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */ + req->op_code = MM_ROC_OP_CANCEL; + + /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL); +} + +int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr, + struct mm_set_power_cfm *cfm) +{ + struct mm_set_power_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_POWER_REQ message */ + req = rwnx_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_power_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_SET_POWER_REQ message */ + req->inst_nbr = vif_idx; + req->power = pwr; + + /* Send the MM_SET_POWER_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_POWER_CFM, cfm); +} + +int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param, + bool uapsd, u8 inst_nbr) +{ + struct mm_set_edca_req *set_edca_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_EDCA_REQ message */ + set_edca_req = rwnx_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_edca_req)); + if (!set_edca_req) + return -ENOMEM; + + /* Set parameters for the MM_SET_EDCA_REQ message */ + set_edca_req->ac_param = param; + set_edca_req->uapsd = uapsd; + set_edca_req->hw_queue = hw_queue; + set_edca_req->inst_nbr = inst_nbr; + + /* Send the MM_SET_EDCA_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL); +} + +#ifdef AICWF_ARP_OFFLOAD +int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, u32 ipaddr, + u8 enable) +{ + struct mm_set_arpoffload_en_req *arp_offload_req; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_P2P_NOA_REQ message */ + arp_offload_req = + rwnx_msg_zalloc(MM_SET_ARPOFFLOAD_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_arpoffload_en_req)); + + if (!arp_offload_req) + return -ENOMEM; + + /* Fill the message parameters */ + arp_offload_req->enable = enable; + arp_offload_req->vif_idx = rwnx_vif->vif_index; + arp_offload_req->ipaddr = ipaddr; + + /* Send the MM_ARPOFFLOAD_EN_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, arp_offload_req, 1, MM_SET_ARPOFFLOAD_CFM, NULL); + + return error; +} +#endif + +int rwnx_send_disable_agg_req(struct rwnx_hw *rwnx_hw, u8 agg_disable, + u8 agg_disable_rx, u8 sta_idx) +{ + struct mm_set_agg_disable_req *req; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_AGG_DISABLE_REQ message */ + req = rwnx_msg_zalloc(MM_SET_AGG_DISABLE_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_agg_disable_req)); + + if (!req) + return -ENOMEM; + + req->disable = agg_disable; + req->staidx = sta_idx; + req->disable_rx = agg_disable_rx; + + /* Send the MM_SET_AGG_DISABLE_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_AGG_DISABLE_CFM, NULL); + + return error; +}; + +int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8 disable_coexnull, + u8 enable_nullcts) +{ + struct mm_set_coex_req *coex_req; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_COEX_REQ message */ + coex_req = rwnx_msg_zalloc(MM_SET_COEX_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_coex_req)); + + if (!coex_req) + return -ENOMEM; + + coex_req->bt_on = 1; + coex_req->disable_coexnull = disable_coexnull; + coex_req->enable_nullcts = enable_nullcts; + coex_req->enable_periodic_timer = 0; + coex_req->coex_timeslot_set = 0; + + /* Send the MM_SET_COEX_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, coex_req, 1, MM_SET_COEX_CFM, NULL); + + return error; +}; + +int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8 ofst, u8 sel, + u8 *tbl, u16 len) +{ + struct mm_set_rf_config_req *rf_config_req; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_RF_CONFIG_REQ message */ + rf_config_req = rwnx_msg_zalloc(MM_SET_RF_CONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_rf_config_req)); + + if (!rf_config_req) + return -ENOMEM; + + rf_config_req->table_sel = sel; + rf_config_req->table_ofst = ofst; + rf_config_req->table_num = 16; + rf_config_req->deft_page = 0; + + memcpy(rf_config_req->data, tbl, len); + + /* Send the MM_SET_RF_CONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, rf_config_req, 1, MM_SET_RF_CONFIG_CFM, NULL); + + return error; +} + +int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw, + struct mm_set_rf_calib_cfm *cfm) +{ + struct mm_set_rf_calib_req *rf_calib_req; + struct xtal_cap_conf xtal_cap = { + 0, + }; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_P2P_NOA_REQ message */ + rf_calib_req = rwnx_msg_zalloc(MM_SET_RF_CALIB_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_rf_calib_req)); + + if (!rf_calib_req) + return -ENOMEM; + + aic_chip_set_rf_calib_cfg(rwnx_hw, rf_calib_req); + + rf_calib_req->param_alpha = 0x0c34c008; + rf_calib_req->bt_calib_en = 0; + rf_calib_req->bt_calib_param = 0x264203; + + get_userconfig_xtal_cap(&xtal_cap); + + if (xtal_cap.enable) { + AICWFDBG(LOGDEBUG, "user xtal cap: %d, cap_fine: %d\n", xtal_cap.xtal_cap, + xtal_cap.xtal_cap_fine); + rf_calib_req->xtal_cap = xtal_cap.xtal_cap; + rf_calib_req->xtal_cap_fine = xtal_cap.xtal_cap_fine; + } else { + rf_calib_req->xtal_cap = 0; + rf_calib_req->xtal_cap_fine = 0; + } + + /* Send the MM_SET_RF_CALIB_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, rf_calib_req, 1, MM_SET_RF_CALIB_CFM, cfm); + + return error; +}; + +int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw, + struct mm_get_mac_addr_cfm *cfm) +{ + struct mm_get_mac_addr_req *get_macaddr_req; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_GET_MAC_ADDR_REQ message */ + get_macaddr_req = rwnx_msg_zalloc(MM_GET_MAC_ADDR_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_get_mac_addr_req)); + + if (!get_macaddr_req) + return -ENOMEM; + + get_macaddr_req->get = 1; + + /* Send the MM_GET_MAC_ADDR_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, get_macaddr_req, 1, MM_GET_MAC_ADDR_CFM, cfm); + + return error; +}; + +int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8 sta_idx, + struct mm_get_sta_info_cfm *cfm) +{ + struct mm_get_sta_info_req *get_info_req; + int error; + + /* Build the MM_GET_STA_INFO_REQ message */ + get_info_req = rwnx_msg_zalloc(MM_GET_STA_INFO_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_get_sta_info_req)); + + if (!get_info_req) + return -ENOMEM; + + get_info_req->sta_idx = sta_idx; + + /* Send the MM_GET_STA_INFO_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, get_info_req, 1, MM_GET_STA_INFO_CFM, cfm); + + return error; +}; + +int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8 on, + u8 efuse_valid, u8 set_vendor_info, + u8 fwtrace_redir_en, + struct mm_set_stack_start_cfm *cfm) +{ + struct mm_set_stack_start_req *req; + int error; + + /* Build the MM_SET_STACK_START_REQ message */ + req = rwnx_msg_zalloc(MM_SET_STACK_START_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_stack_start_req)); + + if (!req) + return -ENOMEM; + + req->is_stack_start = on; + req->efuse_valid = efuse_valid; + req->set_vendor_info = set_vendor_info; + req->fwtrace_redir = fwtrace_redir_en; + /* Send the MM_SET_STACK_START_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_STACK_START_CFM, cfm); + + return error; +} + +int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, + struct mm_set_vendor_swconfig_cfm *cfm) +{ + struct mm_set_vendor_swconfig_req *req; + int ret; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the TEMP_COMP_GET_REQ message */ + req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_vendor_swconfig_req)); + if (!req) { + AICWFDBG(LOGINFO, "%s msg_alloc fail\n", __func__); + return -ENOMEM; + } + req->swconfig_id = TEMP_COMP_GET_REQ; + + ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, cfm); + if (!ret) { + AICWFDBG(LOGDEBUG, "status=%d, temp=%d\n", cfm->temp_comp_get_cfm.status, + cfm->temp_comp_get_cfm.degree); + } else { + AICWFDBG(LOGINFO, "%s msg_fail\n", __func__); + return ret; + } +#ifdef CONFIG_AIC8800_TEMP_CONTROL + // rwnx_hw->started_jiffies = jiffies; + rwnx_hw->temp = cfm->temp_comp_get_cfm.degree; +#endif + + return ret; +} + +int rwnx_send_get_temp_hwconfig_req(struct rwnx_hw *rwnx_hw, + struct mm_set_vendor_hwconfig_cfm *cfm) +{ + struct mm_get_chip_temp_req *req; + int ret; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + /* Build the TEMP_COMP_HWCONFIG_GET_REQ message */ + req = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_get_chip_temp_req)); + if (!req) { + AICWFDBG(LOGINFO, "%s msg_alloc fail\n", __func__); + return -ENOMEM; + } + req->hwconfig_id = CHIP_TEMP_GET_REQ; + + ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_HWCONFIG_CFM, cfm); + if (!ret) { + AICWFDBG(LOGDEBUG, "get_chip_temp degree=%d\n", + cfm->chip_temp_cfm.degree); + } else { + AICWFDBG(LOGINFO, "%s msg_fail\n", __func__); + return ret; + } + return ret; +} + +int rwnx_send_set_temp_comp_req(struct rwnx_hw *rwnx_hw, + struct mm_set_vendor_swconfig_cfm *cfm) +{ + struct mm_set_vendor_swconfig_req *req; + int ret; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + /* Build the TEMP_COMP_SET_REQ message */ + req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_vendor_swconfig_req)); + if (!req) { + AICWFDBG(LOGERROR, "%s msg_alloc fail\n", __func__); + return -ENOMEM; + } + req->swconfig_id = TEMP_COMP_SET_REQ; + req->temp_comp_set_req.enable = 1; +#ifdef CONFIG_AIC8800_TEMP_CONTROL + req->temp_comp_set_req.tmr_period_ms = TEMP_GET_INTERVAL; +#else + req->temp_comp_set_req.tmr_period_ms = 15 * 1000; +#endif + + ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, cfm); + if (!ret) { + AICWFDBG(LOGINFO, "temp_comp status: %d\n", cfm->temp_comp_set_cfm.status); + } else { + AICWFDBG(LOGINFO, "%s msg_fail\n", __func__); + return ret; + } + return ret; +} + +int rwnx_send_vendor_hwconfig_req(struct rwnx_hw *rwnx_hw, u32 hwconfig_id, + s32 *param, s32 *param_out) +{ + struct mm_set_acs_txop_req *req0; + struct mm_set_channel_access_req *req1; + struct mm_set_mac_timescale_req *req2; + struct mm_set_cca_threshold_req *req3; + struct mm_set_bwmode_req *req4; + struct mm_set_ap_ps_level_req *req5 = NULL; + struct mm_set_customized_freq_req *req6; + + int error = 0; + int i; + + switch (hwconfig_id) { + case ACS_TXOP_REQ: + /* Build the ACS_TXOP_REQ message */ + req0 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_acs_txop_req)); + if (!req0) + return -ENOMEM; + req0->hwconfig_id = hwconfig_id; + req0->txop_be = param[0]; + req0->txop_bk = param[1]; + req0->txop_vi = param[2]; + req0->txop_vo = param[3]; + AICWFDBG(LOGDEBUG, "set_acs_txop_req: be: %x,bk: %x,vi: %x,vo: %x\n", req0->txop_be, + req0->txop_bk, req0->txop_vi, req0->txop_vo); + /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req0, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + break; + + case CHANNEL_ACCESS_REQ: + /* Build the CHANNEL_ACCESS_REQ message */ + req1 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_channel_access_req)); + if (!req1) + return -ENOMEM; + req1->hwconfig_id = hwconfig_id; + req1->edca[0] = param[0]; + req1->edca[1] = param[1]; + req1->edca[2] = param[2]; + req1->edca[3] = param[3]; + req1->vif_idx = param[4]; + req1->retry_cnt = param[5]; + req1->rts_en = param[6]; + req1->long_nav_en = param[7]; + req1->cfe_en = param[8]; + req1->rc_retry_cnt[0] = param[9]; + req1->rc_retry_cnt[1] = param[10]; + req1->rc_retry_cnt[2] = param[11]; + req1->ccademod_th = param[12]; + AICWFDBG(LOGDEBUG, + "set_channel_access_req:edca[]= %x %x %x %x\nvif_idx: %x, retry_cnt: %x, rts_en: %x, long_nav_en: %x, cfe_en: %x, rc_retry_cnt: %x:%x:%x, ccademod_th = %d\n", + req1->edca[0], req1->edca[1], req1->edca[2], req1->edca[3], + req1->vif_idx, req1->retry_cnt, req1->rts_en, req1->long_nav_en, + req1->cfe_en, req1->rc_retry_cnt[0], req1->rc_retry_cnt[1], + req1->rc_retry_cnt[2], req1->ccademod_th); + /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req1, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + break; + + case MAC_TIMESCALE_REQ: + /* Build the MAC_TIMESCALE_REQ message */ + req2 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_mac_timescale_req)); + if (!req2) + return -ENOMEM; + req2->hwconfig_id = hwconfig_id; + req2->sifs_a_time = param[0]; + req2->sifs_b_time = param[1]; + req2->slot_time = param[2]; + req2->rx_startdelay_ofdm = param[3]; + req2->rx_startdelay_long = param[4]; + req2->rx_startdelay_short = param[5]; + AICWFDBG(LOGDEBUG, + "set_mac_timescale_req:sifsA_time: %x, sifsB_time: %x, slot_time: %x, rx_startdelay ofdm:%x long %x short %x\n", + req2->sifs_a_time, req2->sifs_b_time, req2->slot_time, + req2->rx_startdelay_ofdm, req2->rx_startdelay_long, + req2->rx_startdelay_short); + /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req2, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + break; + + case CCA_THRESHOLD_REQ: + /* Build the CCA_THRESHOLD_REQ message */ + req3 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_cca_threshold_req)); + if (!req3) + return -ENOMEM; + req3->hwconfig_id = hwconfig_id; + req3->auto_cca_en = param[0]; + req3->cca20p_rise_th = param[1]; + req3->cca20s_rise_th = param[2]; + req3->cca20p_fall_th = param[3]; + req3->cca20s_fall_th = param[4]; + AICWFDBG(LOGDEBUG, + "cca_threshold_req: auto_cca_en:%d\ncca20p_rise_th = %d\ncca20s_rise_th = %d\ncca20p_fall_th = %d\ncca20s_fall_th = %d\n", + req3->auto_cca_en, req3->cca20p_rise_th, req3->cca20s_rise_th, + req3->cca20p_fall_th, req3->cca20s_fall_th); + /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req3, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + break; + case BWMODE_REQ: + /* Build the SET_BWMODE_REQ message */ + req4 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_bwmode_req)); + if (!req4) + return -ENOMEM; + req4->hwconfig_id = hwconfig_id; + req4->bwmode = param[0]; + AICWFDBG(LOGDEBUG, "bwmode :%d\n", req4->bwmode); + /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req4, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + break; + case CHIP_TEMP_GET_REQ: + aic_chip_get_temp(rwnx_hw, param_out); + break; + case AP_PS_LEVEL_SET_REQ: + /* + * AP_PS_CLK_1 = 1, // 3:7 -> active 3, doze 7 + * AP_PS_CLK_2, // 5:5 -> active 5, doze 5 + * AP_PS_CLK_3, // 7:3 -> active 7, doze 3 + * AP_PS_CLK_4, // 8:2 -> active 8, doze 2 + * AP_PS_CLK_5, // 9:1 -> active 9, doze 1 + */ + /* Build the MM_SET_VENDOR_HWCONFIG_REQ message */ + req5 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_ap_ps_level_req)); + if (!req5) + return -ENOMEM; + req5->hwconfig_id = hwconfig_id; + req5->ap_ps_level = param[0]; + + /* Send the MM_GET_MAC_ADDR_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req5, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + if (!error) + AICWFDBG(LOGDEBUG, "set ap_ps_level=%d\n", param[0]); + break; + + case CUSTOMIZED_FREQ_REQ: + /* Build the CUSTOMIZED_FREQ_REQ message */ + req6 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_customized_freq_req)); + if (!req6) + return -ENOMEM; + req5->hwconfig_id = hwconfig_id; + for (i = 0; i < 4; i++) { + req6->raw_freq[i] = param[i]; + req6->map_freq[i] = param[i + 4]; + } + AICWFDBG(LOGDEBUG, + "customized_freq_req: raw_freq[ %d %d %d %d ], map_freq[ %d %d %d %d]\n", + param[0], param[1], param[2], param[3], param[4], param[5], + param[6], param[7]); + /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req5, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL); + + break; + default: + return -ENOMEM; + } + return error; +} + +int rwnx_send_vendor_swconfig_req(struct rwnx_hw *rwnx_hw, u32 swconfig_id, + s32 *param_in, s32 *param_out) +{ + struct mm_set_vendor_swconfig_req *req; + struct mm_set_vendor_swconfig_cfm cfm = { + 0, + }; + int error; + + req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_vendor_swconfig_req)); + if (!req) + return -ENOMEM; + req->swconfig_id = swconfig_id; + + switch (swconfig_id) { + case BCN_CFG_REQ: + /* Build the BCN_CFG_REQ message */ + req->bcn_cfg_req.tim_bcmc_ignored_enable = (bool)param_in[0]; + AICWFDBG(LOGDEBUG, "bcn_cfg_req: tim_bcmc_ignd=%d\n", + req->bcn_cfg_req.tim_bcmc_ignored_enable); + /* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm); + if (!error) { + param_out[0] = (s32)cfm.bcn_cfg_cfm.tim_bcmc_ignored_status; + AICWFDBG(LOGDEBUG, "status=%d\n", cfm.bcn_cfg_cfm.tim_bcmc_ignored_status); + } + break; + + case TEMP_COMP_SET_REQ: + /* Build the TEMP_COMP_SET_REQ message */ + req->temp_comp_set_req.enable = (u8)param_in[0]; + req->temp_comp_set_req.tmr_period_ms = (u32)param_in[1]; + AICWFDBG(LOGDEBUG, "temp_comp_set_req: en=%d, tmr=%x\n", + req->temp_comp_set_req.enable, + req->temp_comp_set_req.tmr_period_ms); + /* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm); + if (!error) { + param_out[0] = (s32)cfm.temp_comp_set_cfm.status; + AICWFDBG(LOGDEBUG, "status=%d\n", cfm.temp_comp_set_cfm.status); + } + break; + + case TEMP_COMP_GET_REQ: + AICWFDBG(LOGDEBUG, "temp_comp_get_req\n"); + /* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm); + if (!error) { + param_out[0] = (s32)cfm.temp_comp_get_cfm.status; + param_out[1] = (s32)cfm.temp_comp_get_cfm.degree; + AICWFDBG(LOGDEBUG, "status=%d, degree=%d\n", cfm.temp_comp_get_cfm.status, + cfm.temp_comp_get_cfm.degree); + } + break; + + case EXT_FLAGS_SET_REQ: + /* Build the EXT_FLAGS_SET_REQ message */ + req->ext_flags_set_req.user_flags = (u32)param_in[0]; + AICWFDBG(LOGDEBUG, "ext_flags_set_req: flags=%x\n", + req->ext_flags_set_req.user_flags); + /* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm); + if (!error) { + param_out[0] = (u32)cfm.ext_flags_set_cfm.user_flags; + AICWFDBG(LOGDEBUG, "cfm flags=%x\n", cfm.ext_flags_set_cfm.user_flags); + } + break; + + case EXT_FLAGS_GET_REQ: + /* Build the EXT_FLAGS_GET_REQ message */ + AICWFDBG(LOGDEBUG, "ext_flags_get_req\n"); + /* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm); + if (!error) { + param_out[0] = (u32)cfm.ext_flags_get_cfm.user_flags; + AICWFDBG(LOGDEBUG, "cfm flags=%x\n", cfm.ext_flags_get_cfm.user_flags); + } + break; + + case EXT_FLAGS_MASK_SET_REQ: + /* Build the EXT_FLAGS_MASK_SET_REQ message */ + req->ext_flags_mask_set_req.user_flags_mask = (u32)param_in[0]; + req->ext_flags_mask_set_req.user_flags_val = (u32)param_in[1]; + AICWFDBG(LOGDEBUG, "ext_flags_mask_set_req: flags mask=0x%x, val=0x%x\n", + req->ext_flags_mask_set_req.user_flags_mask, + req->ext_flags_mask_set_req.user_flags_val); + /* Send the MM_SET_VENDOR_SWCONFIG_REQ message to UMAC FW */ + error = + rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, &cfm); + if (!error) { + param_out[0] = (u32)cfm.ext_flags_mask_set_cfm.user_flags; + AICWFDBG(LOGDEBUG, "cfm flags=%x\n", cfm.ext_flags_mask_set_cfm.user_flags); + } + break; + + default: + error = -ENOMEM; + break; + } + + return error; +} + +int rwnx_send_mask_set_ext_flags_req(struct rwnx_hw *rwnx_hw, + u32 flags_mask, u32 flags_val, + struct mm_set_vendor_swconfig_cfm *cfm) +{ + struct mm_set_vendor_swconfig_req *req; + int ret; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + /* Build the REQ message */ + req = rwnx_msg_zalloc(MM_SET_VENDOR_SWCONFIG_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_vendor_swconfig_req)); + if (!req) { + AICWFDBG(LOGERROR, "%s msg_alloc fail\n", __func__); + return -ENOMEM; + } + req->swconfig_id = EXT_FLAGS_MASK_SET_REQ; + req->ext_flags_mask_set_req.user_flags_mask = (u32)flags_mask; + req->ext_flags_mask_set_req.user_flags_val = (u32)flags_val; + + ret = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_SWCONFIG_CFM, cfm); + if (!ret) { + AICWFDBG(LOGINFO, "curr ext_flags=%x\n", + cfm->ext_flags_mask_set_cfm.user_flags); + } else { + AICWFDBG(LOGERROR, "%s send_msg_fail\n", __func__); + return ret; + } + return ret; +} + +int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, + struct mm_get_fw_version_cfm *cfm) +{ + void *req; + int error; + + /* Build the MM_GET_FW_VERSION_REQ message */ + req = rwnx_msg_zalloc(MM_GET_FW_VERSION_REQ, TASK_MM, DRV_TASK_ID, + sizeof(u8)); + + if (!req) + return -ENOMEM; + + /* Send the MM_GET_FW_VERSION_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, req, 1, MM_GET_FW_VERSION_CFM, cfm); + + return error; +} + +int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw) +{ + struct mm_set_txpwr_lvl_req *txpwr_lvl_req; + struct txpwr_lvl_conf_v2 txpwr_lvl_v2_tmp; + struct txpwr_lvl_conf_v2 *txpwr_lvl_v2; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_TXPWR_LVL_REQ message */ + txpwr_lvl_req = + rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_txpwr_lvl_req)); + + if (!txpwr_lvl_req) + return -ENOMEM; + + txpwr_lvl_v2 = &txpwr_lvl_v2_tmp; + + get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_v2); + + if (txpwr_lvl_v2->enable == 0) { + rwnx_msg_free(rwnx_hw, txpwr_lvl_req); + return 0; + } + + AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]); + AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]); + AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[0]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[1]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[2]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[3]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[4]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[5]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[6]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[7]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[8]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[9]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[10]); + AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[11]); + + if (rwnx_hw->testmode == 0 && chip_sub_id == 0) { + txpwr_lvl_req->txpwr_lvl.enable = txpwr_lvl_v2->enable; + txpwr_lvl_req->txpwr_lvl.dsss = + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]; // 11M + txpwr_lvl_req->txpwr_lvl.ofdmlowrate_2g4 = + txpwr_lvl_v2->pwrlvl_11ax_2g4[4]; // MCS4 + txpwr_lvl_req->txpwr_lvl.ofdm64qam_2g4 = + txpwr_lvl_v2->pwrlvl_11ax_2g4[7]; // MCS7 + txpwr_lvl_req->txpwr_lvl.ofdm256qam_2g4 = + txpwr_lvl_v2->pwrlvl_11ax_2g4[9]; // MCS9 + txpwr_lvl_req->txpwr_lvl.ofdm1024qam_2g4 = + txpwr_lvl_v2->pwrlvl_11ax_2g4[11]; // MCS11 + txpwr_lvl_req->txpwr_lvl.ofdmlowrate_5g = 13; // unused + txpwr_lvl_req->txpwr_lvl.ofdm64qam_5g = 13; // unused + txpwr_lvl_req->txpwr_lvl.ofdm256qam_5g = 13; // unused + txpwr_lvl_req->txpwr_lvl.ofdm1024qam_5g = 13; // unused + } else { + txpwr_lvl_req->txpwr_lvl_v2 = *txpwr_lvl_v2; + } + + /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, + MM_SET_TXPWR_IDX_LVL_CFM, NULL); + + return error; +} + +int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw, int index_reg) +{ + struct mm_set_txpwr_lvl_req *txpwr_lvl_req; + struct txpwr_lvl_conf_v3 txpwr_lvl_v3_tmp; + struct txpwr_lvl_conf_v3 *txpwr_lvl_v3; + struct txpwr_loss_conf txpwr_loss_tmp; + struct txpwr_loss_conf *txpwr_loss; + int error; + int i; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_TXPWR_LVL_REQ message */ + txpwr_lvl_req = + rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_txpwr_lvl_req)); + + if (!txpwr_lvl_req) + return -ENOMEM; + + txpwr_lvl_v3 = &txpwr_lvl_v3_tmp; + txpwr_loss = &txpwr_loss_tmp; + txpwr_loss->loss_enable_2g4 = 0; + txpwr_loss->loss_enable_5g = 0; + + //get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_v3); + get_userconfig_txpwr_lvl_group_in_fdrv(txpwr_lvl_v3, index_reg); + get_userconfig_txpwr_loss(txpwr_loss); + + if (txpwr_loss->loss_enable_2g4 == 1) { + AICWFDBG(LOGINFO, "%s:loss_value_2g4: %d\r\n", __func__, + txpwr_loss->loss_value_2g4); + + for (i = 0; i <= 11; i++) + txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[i] -= txpwr_loss->loss_value_2g4; + for (i = 0; i <= 9; i++) + txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[i] -= txpwr_loss->loss_value_2g4; + for (i = 0; i <= 11; i++) + txpwr_lvl_v3->pwrlvl_11ax_2g4[i] -= txpwr_loss->loss_value_2g4; + } + + if (txpwr_loss->loss_enable_5g == 1) { + AICWFDBG(LOGINFO, "%s:loss_value_5g: %d\r\n", __func__, + txpwr_loss->loss_value_5g); + + for (i = 0; i <= 11; i++) + txpwr_lvl_v3->pwrlvl_11a_5g[i] -= txpwr_loss->loss_value_5g; + for (i = 0; i <= 9; i++) + txpwr_lvl_v3->pwrlvl_11n_11ac_5g[i] -= txpwr_loss->loss_value_5g; + for (i = 0; i <= 11; i++) + txpwr_lvl_v3->pwrlvl_11ax_5g[i] -= txpwr_loss->loss_value_5g; + } + + if (txpwr_lvl_v3->enable == 0) { + rwnx_msg_free(rwnx_hw, txpwr_lvl_req); + return 0; + } + + AICWFDBG(LOGDEBUG, "%s: dump\n", __func__); + AICWFDBG(LOGDEBUG, "txpwr_lvl_v3->enable: %d\r\n", txpwr_lvl_v3->enable); + AICWFDBG(LOGDEBUG, "lvl_11b_11ag_1m_2g4:%d\r\n", + txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]); + + txpwr_lvl_req->txpwr_lvl_v3 = *txpwr_lvl_v3; + + /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, + MM_SET_TXPWR_IDX_LVL_CFM, NULL); + + return error; +} + +int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw) +{ + struct mm_set_txpwr_lvl_adj_req *txpwr_lvl_adj_req; + struct txpwr_lvl_adj_conf txpwr_lvl_adj_tmp; + struct txpwr_lvl_adj_conf *txpwr_lvl_adj; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_TXPWR_LVL_REQ message */ + txpwr_lvl_adj_req = + rwnx_msg_zalloc(MM_SET_TXPWR_LVL_ADJ_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_txpwr_lvl_adj_req)); + + if (!txpwr_lvl_adj_req) + return -ENOMEM; + + txpwr_lvl_adj = &txpwr_lvl_adj_tmp; + + get_userconfig_txpwr_lvl_adj_in_fdrv(txpwr_lvl_adj); + + if (txpwr_lvl_adj->enable == 0) { + rwnx_msg_free(rwnx_hw, txpwr_lvl_adj_req); + return 0; + } + + AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_adj->enable); + AICWFDBG(LOGINFO, "%s:lvl_adj_2g4_chan_1_4:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]); + AICWFDBG(LOGINFO, "%s:lvl_adj_2g4_chan_5_9:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[1]); + AICWFDBG(LOGINFO, "%s:lvl_adj_2g4_chan_10_13:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[2]); + AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_42:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_5g[0]); + AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_58:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_5g[1]); + AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_106:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_5g[2]); + AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_122:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_5g[3]); + AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_138:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_5g[4]); + AICWFDBG(LOGINFO, "%s:lvl_adj_5g_chan_155:%d\r\n", __func__, + txpwr_lvl_adj->pwrlvl_adj_tbl_5g[5]); + + txpwr_lvl_adj_req->txpwr_lvl_adj = *txpwr_lvl_adj; + + /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, txpwr_lvl_adj_req, 1, + MM_SET_TXPWR_LVL_ADJ_CFM, NULL); + + return error; +} + +int rwnx_send_txpwr_idx_req(struct rwnx_hw *rwnx_hw) +{ + struct mm_set_txpwr_idx_req *txpwr_idx_req; + struct txpwr_idx_conf *txpwr_idx; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_TXPWR_IDX_LVL_REQ message */ + txpwr_idx_req = + rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_txpwr_idx_req)); + + if (!txpwr_idx_req) + return -ENOMEM; + + txpwr_idx = &txpwr_idx_req->txpwr_idx; + txpwr_idx->enable = 1; + txpwr_idx->dsss = 9; + txpwr_idx->ofdmlowrate_2g4 = 8; + txpwr_idx->ofdm64qam_2g4 = 8; + txpwr_idx->ofdm256qam_2g4 = 8; + txpwr_idx->ofdm1024qam_2g4 = 8; + txpwr_idx->ofdmlowrate_5g = 11; + txpwr_idx->ofdm64qam_5g = 10; + txpwr_idx->ofdm256qam_5g = 9; + txpwr_idx->ofdm1024qam_5g = 9; + + get_userconfig_txpwr_idx(txpwr_idx); + + if (txpwr_idx->enable == 0) { + rwnx_msg_free(rwnx_hw, txpwr_idx_req); + return 0; + } + /* Send the MM_SET_TXPWR_IDX_LVL_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, txpwr_idx_req, 1, + MM_SET_TXPWR_IDX_LVL_CFM, NULL); + + return error; +}; + +int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw) +{ + struct mm_set_txpwr_ofst_req *txpwr_ofst_req; + struct txpwr_ofst_conf *txpwr_ofst; + int error; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_TXPWR_OFST_REQ message */ + txpwr_ofst_req = + rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_txpwr_ofst_req)); + + if (!txpwr_ofst_req) + return -ENOMEM; + + txpwr_ofst = &txpwr_ofst_req->txpwr_ofst; + txpwr_ofst->enable = 1; + txpwr_ofst->chan_1_4 = 0; + txpwr_ofst->chan_5_9 = 0; + txpwr_ofst->chan_10_13 = 0; + txpwr_ofst->chan_36_64 = 0; + txpwr_ofst->chan_100_120 = 0; + txpwr_ofst->chan_122_140 = 0; + txpwr_ofst->chan_142_165 = 0; + + aic_chip_get_userconfig_txpwr_ofst(rwnx_hw, txpwr_ofst); + + if (txpwr_ofst->enable == 0) { + rwnx_msg_free(rwnx_hw, txpwr_ofst_req); + return 0; + } + /* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM, + NULL); + + return error; +}; + +int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw) +{ + struct mm_set_txpwr_ofst_req *txpwr_ofst_req; + struct txpwr_ofst2x_conf *txpwr_ofst2x; + int error = 0; + int type, ch_grp; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_SET_TXPWR_OFST_REQ message */ + txpwr_ofst_req = + rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_set_txpwr_ofst_req)); + + if (!txpwr_ofst_req) + return -ENOMEM; + + txpwr_ofst2x = &txpwr_ofst_req->txpwr_ofst2x; + txpwr_ofst2x->enable = 0; + for (type = 0; type < 3; type++) { + for (ch_grp = 0; ch_grp < 6; ch_grp++) { + if (ch_grp < 3) + txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp] = 0; + txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp] = 0; + } + } + + aic_chip_get_userconfig_txpwr_ofst2x(rwnx_hw, txpwr_ofst2x); + + if (txpwr_ofst2x->enable) { + AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_ofst2x->enable); + AICWFDBG(LOGDEBUG, + "pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n chan=\t1-4\t5-9\t10-13"); + for (type = 0; type < 3; type++) { + AICWFDBG(LOGDEBUG, "\n [%d] =", type); + for (ch_grp = 0; ch_grp < 3; ch_grp++) { + AICWFDBG(LOGDEBUG, "\t%d", + txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]); + } + } + AICWFDBG(LOGDEBUG, + "\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n chan=\t36-50\t51-64\t98-114\t115-130\t131-146\t147-166"); + for (type = 0; type < 3; type++) { + AICWFDBG(LOGDEBUG, "\n [%d] =", type); + for (ch_grp = 0; ch_grp < 6; ch_grp++) + AICWFDBG(LOGDEBUG, "\t%d", + txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]); + } + AICWFDBG(LOGDEBUG, "\n"); + + /* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */ + error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM, + NULL); + } else { + AICWFDBG(LOGDEBUG, "%s:Do not use txpwr_ofst2x\r\n", __func__); + rwnx_msg_free(rwnx_hw, txpwr_ofst_req); + } + + return error; +} + +/****************************************************************************** + * Control messages handling functions (FULLMAC only) + *****************************************************************************/ +#ifdef CONFIG_RWNX_FULLMAC +int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw) +{ + struct me_config_req *req; + struct wiphy *wiphy = rwnx_hw->wiphy; + struct ieee80211_supported_band *band_2ghz = + wiphy->bands[NL80211_BAND_2GHZ]; + struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_sta_vht_cap *vht_cap; + struct ieee80211_sta_he_cap const *he_cap = NULL; + + u8 *ht_mcs; + int i; + + if (rwnx_hw->chip_ops->use_80_bandwidth) + rwnx_hw->mod_params->use_80 = true; + + if (rwnx_hw->band_5g_support) { + ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap; + vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap; + } else { + ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap; + vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap; + } + + ht_mcs = (u8 *)&ht_cap->mcs; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_CONFIG_REQ message */ + req = rwnx_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_config_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_CONFIG_REQ message */ + req->ht_supp = ht_cap->ht_supported; + req->vht_supp = vht_cap->vht_supported; + req->ht_cap.ht_capa_info = + cpu_to_le16(ht_cap->cap | IEEE80211_HT_CAP_LDPC_CODING); + req->ht_cap.a_mpdu_param = + ht_cap->ampdu_factor | + (ht_cap->ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + for (i = 0; i < sizeof(ht_cap->mcs); i++) + req->ht_cap.mcs_rate[i] = ht_mcs[i]; + req->ht_cap.ht_extended_capa = 0; + req->ht_cap.tx_beamforming_capa = 0; + req->ht_cap.asel_capa = 0; + + if (req->vht_supp) { + req->vht_cap.vht_capa_info = cpu_to_le32(vht_cap->cap); + req->vht_cap.rx_highest = vht_cap->vht_mcs.rx_highest; + req->vht_cap.rx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; + req->vht_cap.tx_highest = vht_cap->vht_mcs.tx_highest; + req->vht_cap.tx_mcs_map = vht_cap->vht_mcs.tx_mcs_map; + } + + he_cap = ieee80211_get_he_iftype_cap(band_2ghz, NL80211_IFTYPE_STATION); + if (he_cap) { + req->he_supp = he_cap->has_he; + for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.mac_cap_info); i++) + req->he_cap.mac_cap_info[i] = he_cap->he_cap_elem.mac_cap_info[i]; + + for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.phy_cap_info); i++) + req->he_cap.phy_cap_info[i] = he_cap->he_cap_elem.phy_cap_info[i]; + + req->he_cap.mcs_supp.rx_mcs_80 = he_cap->he_mcs_nss_supp.rx_mcs_80; + req->he_cap.mcs_supp.tx_mcs_80 = he_cap->he_mcs_nss_supp.tx_mcs_80; + req->he_cap.mcs_supp.rx_mcs_160 = he_cap->he_mcs_nss_supp.rx_mcs_160; + req->he_cap.mcs_supp.tx_mcs_160 = he_cap->he_mcs_nss_supp.tx_mcs_160; + req->he_cap.mcs_supp.rx_mcs_80p80 = + he_cap->he_mcs_nss_supp.rx_mcs_80p80; + req->he_cap.mcs_supp.tx_mcs_80p80 = + he_cap->he_mcs_nss_supp.tx_mcs_80p80; + for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) + req->he_cap.ppe_thres[i] = he_cap->ppe_thres[i]; + + req->he_ul_on = rwnx_hw->mod_params->he_ul_on; + } + req->ps_on = rwnx_hw->mod_params->ps_on; + req->dpsm = rwnx_hw->mod_params->dpsm; + req->tx_lft = rwnx_hw->mod_params->tx_lft; + req->ant_div_on = rwnx_hw->mod_params->ant_div; + if (rwnx_hw->mod_params->use_80) + req->phy_bw_max = PHY_CHNL_BW_80; + else if (rwnx_hw->mod_params->use_2040) + req->phy_bw_max = PHY_CHNL_BW_40; + else + req->phy_bw_max = PHY_CHNL_BW_20; + + wiphy_info(wiphy, "AICWF HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp, + req->vht_supp, req->he_supp); + /* Send the ME_CONFIG_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_CFM, NULL); +} + +int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw) +{ + struct me_chan_config_req *req; + struct wiphy *wiphy = rwnx_hw->wiphy; + int i; +#ifdef CONFIG_AIC8800_POWER_LIMIT + u8 enable = 0; + s8 max_pwr; + struct txpwr_loss_conf txpwr_loss_tmp; + struct txpwr_loss_conf *txpwr_loss; +#endif + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_CHAN_CONFIG_REQ message */ + req = rwnx_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_chan_config_req)); + if (!req) + return -ENOMEM; + +#ifdef CONFIG_AIC8800_POWER_LIMIT + txpwr_loss = &txpwr_loss_tmp; + get_userconfig_txpwr_loss(txpwr_loss); + + if (txpwr_loss->loss_enable_2g4 == 1) + AICWFDBG(LOGINFO, "%s:loss_value_2g4: %d\r\n", __func__, + txpwr_loss->loss_value_2g4); + if (txpwr_loss->loss_enable_5g == 1) + AICWFDBG(LOGINFO, "%s:loss_value_5g: %d\r\n", __func__, + txpwr_loss->loss_value_5g); +#endif + + req->chan2G4_cnt = 0; + if (wiphy->bands[NL80211_BAND_2GHZ]) { + struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ]; + + for (i = 0; i < b->n_channels; i++) { + req->chan2G4[req->chan2G4_cnt].flags = 0; + + if (b->channels[i].flags & IEEE80211_CHAN_DISABLED) + req->chan2G4[req->chan2G4_cnt].flags |= CHAN_DISABLED; + req->chan2G4[req->chan2G4_cnt].flags |= + get_chan_flags(b->channels[i].flags); + req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ; + req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq; + req->chan2G4[req->chan2G4_cnt].tx_power = + chan_to_fw_pwr(b->channels[i].max_power); +#ifdef CONFIG_AIC8800_POWER_LIMIT + max_pwr = get_powerlimit_by_freq(PHY_BAND_2G4, + req->chan2G4[req->chan2G4_cnt].freq, + &enable); + if (txpwr_loss->loss_enable_2g4 == 1) + max_pwr -= txpwr_loss->loss_value_2g4; + if (enable) { + if (req->chan2G4[req->chan2G4_cnt].tx_power > max_pwr) + req->chan2G4[req->chan2G4_cnt].tx_power = max_pwr; + } +#endif + req->chan2G4_cnt++; + if (req->chan2G4_cnt == MAC_DOMAINCHANNEL_24G_MAX) + break; + } + } + + req->chan5G_cnt = 0; + if (wiphy->bands[NL80211_BAND_5GHZ]) { + struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ]; + + for (i = 0; i < b->n_channels; i++) { + req->chan5G[req->chan5G_cnt].flags = 0; + if (b->channels[i].flags & IEEE80211_CHAN_DISABLED) + req->chan5G[req->chan5G_cnt].flags |= CHAN_DISABLED; + req->chan5G[req->chan5G_cnt].flags |= + get_chan_flags(b->channels[i].flags); + req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ; + req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq; + req->chan5G[req->chan5G_cnt].tx_power = + chan_to_fw_pwr(b->channels[i].max_power); +#ifdef CONFIG_AIC8800_POWER_LIMIT + max_pwr = get_powerlimit_by_freq(PHY_BAND_5G, + req->chan5G[req->chan5G_cnt].freq, + &enable); + if (txpwr_loss->loss_enable_5g == 1) + max_pwr -= txpwr_loss->loss_value_5g; + if (enable) { + if (req->chan5G[req->chan5G_cnt].tx_power > max_pwr) + req->chan5G[req->chan5G_cnt].tx_power = max_pwr; + } +#endif + req->chan5G_cnt++; + if (req->chan5G_cnt == MAC_DOMAINCHANNEL_5G_MAX) + break; + } + } + + /* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL); +} + +int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened, + u8 sta_idx) +{ + struct me_set_control_port_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_SET_CONTROL_PORT_REQ message */ + req = rwnx_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_set_control_port_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_SET_CONTROL_PORT_REQ message */ + req->sta_idx = sta_idx; + req->control_port_open = opened; + + /* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL); +} + +int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw, + struct station_parameters *params, const u8 *mac, + u8 inst_nbr, struct me_sta_add_cfm *cfm) +{ + struct me_sta_add_req *req; + struct link_station_parameters *link_sta_params = ¶ms->link_sta_params; + struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[inst_nbr]; + + u8 *ht_mcs = (u8 *)&link_sta_params->ht_capa->mcs; + + int i; + + /* Build the MM_STA_ADD_REQ message */ + req = rwnx_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_sta_add_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_STA_ADD_REQ message */ + memcpy(&req->mac_addr.array[0], mac, ETH_ALEN); + + req->rate_set.length = link_sta_params->supported_rates_len; + for (i = 0; i < link_sta_params->supported_rates_len; i++) + req->rate_set.array[i] = link_sta_params->supported_rates[i]; + + req->flags = 0; + if (link_sta_params->ht_capa) { + const struct ieee80211_ht_cap *ht_capa = link_sta_params->ht_capa; + + req->flags |= STA_HT_CAPA; + req->ht_cap.ht_capa_info = ht_capa->cap_info; + req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info; + for (i = 0; i < sizeof(ht_capa->mcs); i++) + req->ht_cap.mcs_rate[i] = ht_mcs[i]; + req->ht_cap.ht_extended_capa = ht_capa->extended_ht_cap_info; + req->ht_cap.tx_beamforming_capa = ht_capa->tx_BF_cap_info; + req->ht_cap.asel_capa = ht_capa->antenna_selection_info; + } + + if (link_sta_params->vht_capa) { + const struct ieee80211_vht_cap *vht_capa = link_sta_params->vht_capa; + + req->flags |= STA_VHT_CAPA; + req->vht_cap.vht_capa_info = vht_capa->vht_cap_info; + req->vht_cap.rx_highest = vht_capa->supp_mcs.rx_highest; + req->vht_cap.rx_mcs_map = vht_capa->supp_mcs.rx_mcs_map; + req->vht_cap.tx_highest = vht_capa->supp_mcs.tx_highest; + req->vht_cap.tx_mcs_map = vht_capa->supp_mcs.tx_mcs_map; + } + + if (link_sta_params->he_capa) { + const struct ieee80211_he_cap_elem *he_capa = link_sta_params->he_capa; + struct ieee80211_he_mcs_nss_supp *mcs_nss_supp = + (struct ieee80211_he_mcs_nss_supp *)(he_capa + 1); + + req->flags |= STA_HE_CAPA; + for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) + req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i]; + for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) + req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i]; + req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80; + req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80; + req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160; + req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160; + req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80; + req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80; + } + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) + req->flags |= STA_QOS_CAPA; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP)) + req->flags |= STA_MFP_CAPA; + + if (link_sta_params->opmode_notif_used) { + req->flags |= STA_OPMOD_NOTIF; + req->opmode = link_sta_params->opmode_notif_used; + } + + req->aid = cpu_to_le16(params->aid); + req->uapsd_queues = params->uapsd_queues; + req->max_sp_len = params->max_sp * 2; + req->vif_idx = inst_nbr; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) { + req->tdls_sta = true; + if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) && + !rwnx_vif->tdls_chsw_prohibited) + req->tdls_chsw_allowed = true; + if (rwnx_vif->tdls_status == TDLS_SETUP_RSP_TX) + req->tdls_sta_initiator = true; + } + + /* Send the ME_STA_ADD_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_ADD_CFM, cfm); +} + +int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta) +{ + struct me_sta_del_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_STA_DEL_REQ message */ + req = rwnx_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_sta_del_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_STA_DEL_REQ message */ + req->sta_idx = sta_idx; + req->tdls_sta = tdls_sta; + + /* Send the ME_STA_DEL_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_DEL_CFM, NULL); +} + +int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd, + u8 tx_status) +{ + struct me_traffic_ind_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_UTRAFFIC_IND_REQ message */ + req = rwnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_traffic_ind_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_TRAFFIC_IND_REQ message */ + req->sta_idx = sta_idx; + req->tx_avail = tx_status; + req->uapsd = uapsd; + + /* Send the ME_TRAFFIC_IND_REQ to UMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL); +} + +int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw, u8 sta_idx, + struct me_rc_stats_cfm *cfm) +{ + struct me_rc_stats_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_RC_STATS_REQ message */ + req = rwnx_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_rc_stats_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_RC_STATS_REQ message */ + req->sta_idx = sta_idx; + + /* Send the ME_RC_STATS_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_RC_STATS_CFM, cfm); +} + +int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw, u8 sta_idx, u16 rate_cfg) +{ + struct me_rc_set_rate_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_RC_SET_RATE_REQ message */ + req = rwnx_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_rc_set_rate_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_RC_SET_RATE_REQ message */ + req->sta_idx = sta_idx; + req->fixed_rate_cfg = rate_cfg; + + /* Send the ME_RC_SET_RATE_REQ message to FW */ + return rwnx_send_msg(rwnx_hw, req, 0, 0, NULL); +} + +int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode) +{ + struct me_set_ps_mode_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_SET_PS_MODE_REQ message */ + req = rwnx_msg_zalloc(ME_SET_PS_MODE_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_set_ps_mode_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_SET_PS_MODE_REQ message */ + req->ps_state = ps_mode; + + /* Send the ME_SET_PS_MODE_REQ message to FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_PS_MODE_CFM, NULL); +} + +int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level, u8 disable_filter) +{ + struct me_set_lp_level_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + AICWFDBG(LOGDEBUG, "lp_lvl: %d %d\n", lp_level, disable_filter); + /* Build the ME_SET_LP_LEVEL_REQ message */ + req = rwnx_msg_zalloc(ME_SET_LP_LEVEL_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_set_lp_level_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the ME_SET_LP_LEVEL_REQ message */ + req->lp_level = lp_level; + req->disable_filter = disable_filter; + + /* Send the ME_SET_LP_LEVEL_REQ message to FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_LP_LEVEL_CFM, NULL); +} + +int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, + struct cfg80211_connect_params *sme, + struct sm_connect_cfm *cfm) +{ + struct sm_connect_req *req; + int i; + u32 flags = 0; + bool gval = false; + bool pval = false; + + rwnx_vif->wep_enabled = false; + rwnx_vif->wep_auth_err = false; + rwnx_vif->last_auth_type = 0; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the SM_CONNECT_REQ message */ + req = rwnx_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID, + sizeof(struct sm_connect_req)); + if (!req) + return -ENOMEM; + + if (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40 || + sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104) + gval = true; + + if (sme->crypto.n_ciphers_pairwise && + (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40 || + sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)) + pval = true; + + /* Set parameters for the SM_CONNECT_REQ message */ + if (sme->crypto.n_ciphers_pairwise && + (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40 || + sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP || + sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)) + flags |= DISABLE_HT; + + if (sme->crypto.control_port) + flags |= CONTROL_PORT_HOST; + + if (sme->crypto.control_port_no_encrypt) + flags |= CONTROL_PORT_NO_ENC; + + if (use_pairwise_key(&sme->crypto)) + flags |= WPA_WPA2_IN_USE; + + if (sme->mfp == NL80211_MFP_REQUIRED) + flags |= MFP_IN_USE; + + if (rwnx_vif->sta.ap) + flags |= REASSOCIATION; + + req->ctrl_port_ethertype = sme->crypto.control_port_ethertype; + + if (sme->bssid) + memcpy(&req->bssid, sme->bssid, ETH_ALEN); + else + req->bssid = mac_addr_bcst; +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + if (rwnx_hw->ccode.auto_set) { + memcpy(&rwnx_hw->ccode.bssid, &req->bssid, ETH_ALEN); + atomic_set(&rwnx_hw->ccode.link_scan, 1); + } +#endif + req->vif_idx = rwnx_vif->vif_index; + if (sme->channel) { + req->chan.band = sme->channel->band; + req->chan.freq = sme->channel->center_freq; + req->chan.flags = get_chan_flags(sme->channel->flags); + } else { + req->chan.freq = (u16)-1; + } + for (i = 0; i < sme->ssid_len; i++) + req->ssid.array[i] = sme->ssid[i]; + req->ssid.length = sme->ssid_len; + req->flags = flags; + if (WARN_ON(sme->ie_len > sizeof(req->ie_buf))) + goto invalid_param; + if (sme->ie_len) + memcpy(req->ie_buf, sme->ie, sme->ie_len); + req->ie_len = sme->ie_len; + req->listen_interval = rwnx_mod_params.listen_itv; + req->dont_wait_bcmc = !rwnx_mod_params.listen_bcmc; + + /* Set auth_type */ + if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) + req->auth_type = WLAN_AUTH_OPEN; + else if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) + req->auth_type = WLAN_AUTH_OPEN; + else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) + req->auth_type = WLAN_AUTH_SHARED_KEY; + else if (sme->auth_type == NL80211_AUTHTYPE_FT) + req->auth_type = WLAN_AUTH_FT; + else if (sme->auth_type == NL80211_AUTHTYPE_SAE) + req->auth_type = WLAN_AUTH_SAE; + else + goto invalid_param; + + rwnx_vif->sta.auth_type = sme->auth_type; + + /* Set UAPSD queues */ + req->uapsd_queues = rwnx_mod_params.uapsd_queues; + + rwnx_vif->wep_enabled = pval & gval; + + if (rwnx_vif->wep_enabled) + rwnx_vif->last_auth_type = sme->auth_type; + + rwnx_vif->sta.ssid_len = (int)sme->ssid_len; + memset(rwnx_vif->sta.ssid, 0, rwnx_vif->sta.ssid_len + 1); + memcpy(rwnx_vif->sta.ssid, sme->ssid, rwnx_vif->sta.ssid_len); + if (sme->bssid) + memcpy(rwnx_vif->sta.bssid, sme->bssid, ETH_ALEN); + + AICWFDBG(LOGDEBUG, + "AICWF %s drv_vif_index:%d connect to %s(%d) channel:%d auth_type:%d\r\n", + __func__, rwnx_vif->drv_vif_index, rwnx_vif->sta.ssid, + rwnx_vif->sta.ssid_len, req->chan.freq, req->auth_type); + + /* Send the SM_CONNECT_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, SM_CONNECT_CFM, cfm); + +invalid_param: + rwnx_msg_free(rwnx_hw, req); + return -EINVAL; +} + +int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, u16 reason) +{ + struct sm_disconnect_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the SM_DISCONNECT_REQ message */ + req = rwnx_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID, + sizeof(struct sm_disconnect_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the SM_DISCONNECT_REQ message */ + req->reason_code = reason; + req->vif_idx = rwnx_vif->vif_index; + + /* Send the SM_DISCONNECT_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, SM_DISCONNECT_CFM, NULL); +} + +int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, + u16 status) +{ + struct sm_external_auth_required_rsp *rsp; + + /* Build the SM_EXTERNAL_AUTH_CFM message */ + rsp = rwnx_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID, + sizeof(struct sm_external_auth_required_rsp)); + if (!rsp) + return -ENOMEM; + + rsp->status = status; + rsp->vif_idx = rwnx_vif->vif_index; + + /* send the SM_EXTERNAL_AUTH_REQUIRED_RSP message UMAC FW */ + return rwnx_send_msg(rwnx_hw, rsp, 1, SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM, + NULL); +} + +int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct cfg80211_ap_settings *settings, + struct apm_start_cfm *cfm, + struct rwnx_ipc_elem_var *elem) +{ + struct apm_start_req *req; + struct rwnx_bcn *bcn = &vif->ap.bcn; + u8 *buf; + u32 flags = 0; + const u8 *rate_ie; + u8 rate_len = 0; + int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos; + int len, i; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the APM_START_REQ message */ + req = rwnx_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID, + sizeof(struct apm_start_req)); + if (!req) + return -ENOMEM; + + // Build the beacon + bcn->dtim = (u8)settings->dtim_period; + buf = rwnx_build_bcn(bcn, &settings->beacon); + if (!buf) { + rwnx_msg_free(rwnx_hw, req); + return -ENOMEM; + } + + // Retrieve the basic rate set from the beacon buffer + len = bcn->len - var_offset; + var_pos = buf + var_offset; + +// Assume that rate higher that 54 Mbps are BSS membership + + rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); + if (rate_ie) { + const u8 *rates = rate_ie + 2; + u8 rate1 = 0; + bool is_basic1; + + for (i = 0; i < rate_ie[1] && rate_len < MAC_RATESET_LEN; i++) { + rate1 = rates[i]; + is_basic1 = ((rate1 & 0x80) && ((rate1 & ~0x80) <= (54 * 2))); + if (is_basic1) + req->basic_rates.array[rate_len++] = rate1; + } + } + rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len); + if (rate_ie) { + const u8 *rates = rate_ie + 2; + u8 rate2 = 0; + bool is_basic2; + + for (i = 0; i < rate_ie[1] && rate_len < MAC_RATESET_LEN; i++) { + rate2 = rates[i]; + is_basic2 = ((rate2 & 0x80) && ((rate2 & ~0x80) <= (54 * 2))); + if (is_basic2) + req->basic_rates.array[rate_len++] = rate2; + } + } + req->basic_rates.length = rate_len; + + rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len); + + /* Set parameters for the APM_START_REQ message */ + req->vif_idx = vif->vif_index; + req->bcn_addr = elem->dma_addr; + req->bcn_len = bcn->len; + req->tim_oft = bcn->head_len; + req->tim_len = bcn->tim_len; + req->chan.band = settings->chandef.chan->band; + req->chan.freq = settings->chandef.chan->center_freq; + req->chan.flags = 0; + req->chan.tx_power = chan_to_fw_pwr(settings->chandef.chan->max_power); + req->center_freq1 = settings->chandef.center_freq1; + req->center_freq2 = settings->chandef.center_freq2; + req->ch_width = bw2chnl[settings->chandef.width]; + req->bcn_int = settings->beacon_interval; + + if (settings->crypto.control_port) + flags |= CONTROL_PORT_HOST; + + if (settings->crypto.control_port_no_encrypt) + flags |= CONTROL_PORT_NO_ENC; + + if (use_pairwise_key(&settings->crypto)) + flags |= WPA_WPA2_IN_USE; + + if (settings->crypto.control_port_ethertype) + req->ctrl_port_ethertype = settings->crypto.control_port_ethertype; + else + req->ctrl_port_ethertype = cpu_to_be16(ETH_P_PAE); + req->flags = flags; + + /* Send the APM_START_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CFM, cfm); +} + +int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif) +{ + struct apm_stop_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the APM_STOP_REQ message */ + req = rwnx_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID, + sizeof(struct apm_stop_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the APM_STOP_REQ message */ + req->vif_idx = vif->vif_index; + + /* Send the APM_STOP_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CFM, NULL); +} + +#define P2P_WILDCARD_SSID "DIRECT-" +#define P2P_WILDCARD_SSID_LEN (sizeof(P2P_WILDCARD_SSID) - 1) + +#ifdef CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE +u8 vendor_extension_data[256]; +u8 vendor_extension_len; + +static void rwnx_insert_vendor_extension_data(struct scanu_vendor_ie_req *ie_req) +{ + u8 temp_ie[256]; + u8 vendor_extension_subelement[3] = {0x00, 0x37, 0x2A}; + u8 vendor_extension_id[2] = {0x10, 0x49}; + int index = 0; + int vendor_extension_subelement_len = 0; + + memset(temp_ie, 0, 256); + + // find vendor_extension_subelement + for (index = 0; index < ie_req->add_ie_len; index++) { + if (ie_req->ie[index] == vendor_extension_id[0]) { + index++; + if (index == ie_req->add_ie_len) + return; + + if (ie_req->ie[index] == vendor_extension_id[1] && + ie_req->ie[index + 3] == vendor_extension_subelement[0] && + ie_req->ie[index + 4] == vendor_extension_subelement[1] && + ie_req->ie[index + 5] == vendor_extension_subelement[2]) { + index = index + 2; + vendor_extension_subelement_len = ie_req->ie[index]; + AICWFDBG(LOGDEBUG, + "AICWF %s find vendor_extension_subelement,index:%d len:%d\r\n", + __func__, index, ie_req->ie[index]); + break; + } + } + } + index = index + vendor_extension_subelement_len; + + // insert vendor extension + memcpy(&temp_ie[0], ie_req->ie, index + 1); + memcpy(&temp_ie[index + 1], vendor_extension_data, + vendor_extension_len /*sizeof(vendor_extension_data) */); + memcpy(&temp_ie[index + 1 + + vendor_extension_len /*sizeof(vendor_extension_data) */], + &ie_req->ie[index + 1], ie_req->add_ie_len - index); + + memcpy(ie_req->ie, temp_ie, + ie_req->add_ie_len + + vendor_extension_len /*sizeof(vendor_extension_data) */); + ie_req->add_ie_len = + ie_req->add_ie_len + + vendor_extension_len /*sizeof(vendor_extension_data) */; + ie_req->ie[1] = + ie_req->ie[1] + vendor_extension_len /*sizeof(vendor_extension_data) */; + + // rwnx_data_dump((char*)__func__, (void*)ie_req->ie, + // ie_req->add_ie_len); +} +#endif // CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE + +int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, + struct cfg80211_scan_request *param) +{ + struct scanu_start_req *req = NULL; + struct scanu_vendor_ie_req *ie_req = NULL; + struct mm_add_if_cfm add_if_cfm; + int i; + u8 chan_flags = 0; + int err; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the SCANU_START_REQ message */ + req = rwnx_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID, + sizeof(struct scanu_start_req)); + if (!req) + return -ENOMEM; + + rwnx_hw->scanning = 1; + /* Set parameters */ + req->vif_idx = rwnx_vif->vif_index; + req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels); + +#ifdef CONFIG_AIC8800_AUTO_CUSTREG + if (rwnx_hw->ccode.ccode_set || rwnx_hw->ccode.ccode_cnt >= MAX_SEARCH_CNT) + req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids); + else + req->ssid_cnt = (u8)min_t(int, 0, param->n_ssids); +#else + req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids); +#endif + + req->bssid = mac_addr_bcst; + req->no_cck = param->no_cck; + if (param->duration_mandatory) + // req->duration = ieee80211_tu_to_usec(param->duration); + req->duration = 0; + +#ifdef AIC8800_RADAR_OR_IR_DETECT + if (req->ssid_cnt == 0) + chan_flags |= CHAN_NO_IR; +#endif + for (i = 0; i < req->ssid_cnt; i++) { + int j; + + for (j = 0; j < param->ssids[i].ssid_len; j++) + req->ssid[i].array[j] = param->ssids[i].ssid[j]; + req->ssid[i].length = param->ssids[i].ssid_len; + + if (!memcmp(P2P_WILDCARD_SSID, param->ssids[i].ssid, + P2P_WILDCARD_SSID_LEN)) { + AICWFDBG(LOGINFO, "p2p scanu:%d,%d,%d\n", rwnx_vif->vif_index, + rwnx_vif->is_p2p_vif, rwnx_hw->is_p2p_alive); +#ifdef CONFIG_AIC8800_USE_P2P0 + if (rwnx_vif->is_p2p_vif && !rwnx_hw->is_p2p_alive) { +#else + if (rwnx_vif == rwnx_hw->p2p_dev_vif && !rwnx_vif->up) { +#endif + err = rwnx_send_add_if(rwnx_hw, rwnx_vif->wdev.address, + RWNX_VIF_TYPE(rwnx_vif), false, + &add_if_cfm); + if (err) + goto error; + + if (add_if_cfm.status != 0) + return -EIO; + + /* Save the index retrieved from LMAC */ + spin_lock_bh(&rwnx_hw->cb_lock); + rwnx_vif->vif_index = add_if_cfm.inst_nbr; + rwnx_vif->up = true; + rwnx_hw->vif_started++; + rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif; + spin_unlock_bh(&rwnx_hw->cb_lock); + } + rwnx_hw->is_p2p_alive = 1; +#ifndef CONFIG_AIC8800_USE_P2P0 + mod_timer(&rwnx_hw->p2p_alive_timer, + jiffies + msecs_to_jiffies(1000)); + atomic_set(&rwnx_hw->p2p_alive_timer_count, 0); +#endif + AICWFDBG(LOGINFO, "p2p scan start\n"); + } + } + + if (param->ie) { + ie_req = rwnx_msg_zalloc(SCANU_VENDOR_IE_REQ, TASK_SCANU, DRV_TASK_ID, + sizeof(struct scanu_vendor_ie_req)); + if (!ie_req) + return -ENOMEM; + + ie_req->add_ie_len = param->ie_len; + ie_req->vif_idx = rwnx_vif->vif_index; + memcpy(ie_req->ie, param->ie, param->ie_len); +#ifdef CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE + rwnx_insert_vendor_extension_data(ie_req); +#endif // CONFIG_AIC8800_SET_VENDOR_EXTENSION_IE + req->add_ie_len = 0; + req->add_ies = 0; + + err = rwnx_send_msg(rwnx_hw, ie_req, 1, SCANU_VENDOR_IE_CFM, NULL); + if (err) + goto error; + } else { + req->add_ie_len = 0; + req->add_ies = 0; + } + + for (i = 0; i < req->chan_cnt; i++) { + struct ieee80211_channel *chan = param->channels[i]; + + AICWFDBG(LOGDEBUG, "scan channel:%d(%d) \r\n", + ieee80211_frequency_to_channel(chan->center_freq), + chan->center_freq); + req->chan[i].band = chan->band; + req->chan[i].freq = chan->center_freq; + req->chan[i].flags = chan_flags | get_chan_flags(chan->flags); + req->chan[i].tx_power = chan_to_fw_pwr(chan->max_reg_power); + } + + /* Send the SCANU_START_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, SCANU_START_CFM_ADDTIONAL, NULL); +error: + if (req) + rwnx_msg_free(rwnx_hw, req); + if (ie_req) + rwnx_msg_free(rwnx_hw, ie_req); + return -ENOMEM; +} + +int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw, + struct scan_cancel_cfm *cfm) +{ + struct scan_cancel_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the SCAN_CANCEL_REQ message */ + req = rwnx_msg_zalloc(SCANU_CANCEL_REQ, TASK_SCANU, DRV_TASK_ID, + sizeof(struct scan_cancel_req)); + if (!req) + return -ENOMEM; + + /* Send the SCAN_CANCEL_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, SCANU_CANCEL_CFM, cfm); +} + +int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct cfg80211_chan_def *chandef, + struct apm_start_cac_cfm *cfm) +{ + struct apm_start_cac_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the APM_START_CAC_REQ message */ + req = rwnx_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID, + sizeof(struct apm_start_cac_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the APM_START_CAC_REQ message */ + req->vif_idx = vif->vif_index; + req->chan.band = chandef->chan->band; + req->chan.freq = chandef->chan->center_freq; + req->chan.flags = 0; + req->center_freq1 = chandef->center_freq1; + req->center_freq2 = chandef->center_freq2; + req->ch_width = bw2chnl[chandef->width]; + + /* Send the APM_START_CAC_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CAC_CFM, cfm); +} + +int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif) +{ + struct apm_stop_cac_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the APM_STOP_CAC_REQ message */ + req = rwnx_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID, + sizeof(struct apm_stop_cac_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the APM_STOP_CAC_REQ message */ + req->vif_idx = vif->vif_index; + + /* Send the APM_STOP_CAC_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CAC_CFM, NULL); +} + +int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + const struct mesh_config *conf, + const struct mesh_setup *setup, + struct mesh_start_cfm *cfm) +{ + // Message to send + struct mesh_start_req *req; + // Supported basic rates + struct ieee80211_supported_band *band_2ghz = + rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ]; + /* Counter */ + int i; + /* Return status */ + int status; + /* DMA Address to be unmapped after confirmation reception */ + u32 dma_addr = 0; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MESH_START_REQ message */ + req = rwnx_msg_zalloc(MESH_START_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_start_req)); + if (!req) + return -ENOMEM; + + req->vif_index = vif->vif_index; + req->bcn_int = setup->beacon_interval; + req->dtim_period = setup->dtim_period; + req->mesh_id_len = setup->mesh_id_len; + + for (i = 0; i < setup->mesh_id_len; i++) + req->mesh_id[i] = *(setup->mesh_id + i); + + req->user_mpm = setup->user_mpm; + req->is_auth = setup->is_authenticated; + req->auth_id = setup->auth_id; + req->ie_len = setup->ie_len; + + if (setup->ie_len) { + /* + * Need to provide a Virtual Address to the MAC so that it can + * download the additional information elements. + */ + req->ie_addr = dma_map_single(rwnx_hw->dev, (void *)setup->ie, + setup->ie_len, DMA_FROM_DEVICE); + + /* Check DMA mapping result */ + if (dma_mapping_error(rwnx_hw->dev, req->ie_addr)) { + AICWFDBG(LOGDEBUG, KERN_CRIT + "AICWF %s - DMA Mapping error on additional IEs\n", + __func__); + + /* Consider there is no Additional IEs */ + req->ie_len = 0; + } else { + /* Store DMA Address so that we can unmap the memory section + * once MESH_START_CFM is received + */ + dma_addr = req->ie_addr; + } + } + + /* Provide rate information */ + req->basic_rates.length = 0; + for (i = 0; i < band_2ghz->n_bitrates; i++) { + u16 rate = band_2ghz->bitrates[i].bitrate; + + /* Read value is in units of 100 Kbps, provided value is in units + * of 1Mbps, and multiplied by 2 so that 5.5 becomes 11 + */ + rate = (rate << 1) / 10; + + if (setup->basic_rates & CO_BIT(i)) + rate |= 0x80; + + req->basic_rates.array[i] = (u8)rate; + req->basic_rates.length++; + } + + /* Provide channel information */ + req->chan.band = setup->chandef.chan->band; + req->chan.freq = setup->chandef.chan->center_freq; + req->chan.flags = 0; + req->chan.tx_power = chan_to_fw_pwr(setup->chandef.chan->max_power); + req->center_freq1 = setup->chandef.center_freq1; + req->center_freq2 = setup->chandef.center_freq2; + req->ch_width = bw2chnl[setup->chandef.width]; + + /* Send the MESH_START_REQ message to UMAC FW */ + status = rwnx_send_msg(rwnx_hw, req, 1, MESH_START_CFM, cfm); + + /* Unmap DMA area */ + if (setup->ie_len) + dma_unmap_single(rwnx_hw->dev, dma_addr, setup->ie_len, DMA_TO_DEVICE); + + /* Return the status */ + return status; +} + +int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct mesh_stop_cfm *cfm) +{ + // Message to send + struct mesh_stop_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MESH_STOP_REQ message */ + req = rwnx_msg_zalloc(MESH_STOP_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_stop_req)); + if (!req) + return -ENOMEM; + + req->vif_idx = vif->vif_index; + + /* Send the MESH_STOP_REQ message to UMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MESH_STOP_CFM, cfm); +} + +int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + u32 mask, const struct mesh_config *p_mconf, + struct mesh_update_cfm *cfm) +{ + // Message to send + struct mesh_update_req *req; + // Keep only bit for fields which can be updated + u32 supp_mask = (mask << 1) & (CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS) | + CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE) | + CO_BIT(NL80211_MESHCONF_FORWARDING) | + CO_BIT(NL80211_MESHCONF_POWER_MODE)); + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (!supp_mask) + return -ENOENT; + + /* Build the MESH_UPDATE_REQ message */ + req = rwnx_msg_zalloc(MESH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_update_req)); + + if (!req) + return -ENOMEM; + + req->vif_idx = vif->vif_index; + + if (supp_mask & CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS)) { + req->flags |= CO_BIT(MESH_UPDATE_FLAGS_GATE_MODE_BIT); + req->gate_announ = p_mconf->dot11MeshGateAnnouncementProtocol; + } + + if (supp_mask & CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE)) { + req->flags |= CO_BIT(MESH_UPDATE_FLAGS_ROOT_MODE_BIT); + req->root_mode = p_mconf->dot11MeshHWMPRootMode; + } + + if (supp_mask & CO_BIT(NL80211_MESHCONF_FORWARDING)) { + req->flags |= CO_BIT(MESH_UPDATE_FLAGS_MESH_FWD_BIT); + req->mesh_forward = p_mconf->dot11MeshForwarding; + } + + if (supp_mask & CO_BIT(NL80211_MESHCONF_POWER_MODE)) { + req->flags |= CO_BIT(MESH_UPDATE_FLAGS_LOCAL_PSM_BIT); + req->local_ps_mode = p_mconf->power_mode; + } + + /* Send the MESH_UPDATE_REQ message to UMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MESH_UPDATE_CFM, cfm); +} + +int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + u8 sta_idx, struct mesh_peer_info_cfm *cfm) +{ + // Message to send + struct mesh_peer_info_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MESH_PEER_INFO_REQ message */ + req = rwnx_msg_zalloc(MESH_PEER_INFO_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_peer_info_req)); + if (!req) + return -ENOMEM; + + req->sta_idx = sta_idx; + + /* Send the MESH_PEER_INFO_REQ message to UMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MESH_PEER_INFO_CFM, cfm); +} + +void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *vif, u8 sta_idx, + u8 mlink_state) +{ + // Message to send + struct mesh_peer_update_ntf *ntf; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MESH_PEER_UPDATE_NTF message */ + ntf = rwnx_msg_zalloc(MESH_PEER_UPDATE_NTF, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_peer_update_ntf)); + + if (ntf) { + ntf->vif_idx = vif->vif_index; + ntf->sta_idx = sta_idx; + ntf->state = mlink_state; + + /* Send the MESH_PEER_INFO_REQ message to UMAC FW */ + rwnx_send_msg(rwnx_hw, ntf, 0, 0, NULL); + } +} + +void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *vif, u8 *tgt_addr) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Check if we are already waiting for a confirmation */ + if (!vif->ap.create_path) { + // Message to send + struct mesh_path_create_req *req; + + /* Build the MESH_PATH_CREATE_REQ message */ + req = rwnx_msg_zalloc(MESH_PATH_CREATE_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_path_create_req)); + + if (req) { + req->vif_idx = vif->vif_index; + memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN); + + vif->ap.create_path = true; + + /* Send the MESH_PATH_CREATE_REQ message to UMAC FW */ + rwnx_send_msg(rwnx_hw, req, 0, 0, NULL); + } + } +} + +int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *vif, const u8 *tgt_addr, + const u8 *p_nhop_addr, + struct mesh_path_update_cfm *cfm) +{ + // Message to send + struct mesh_path_update_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MESH_PATH_UPDATE_REQ message */ + req = rwnx_msg_zalloc(MESH_PATH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_path_update_req)); + if (!req) + return -ENOMEM; + + req->delete = (!p_nhop_addr); + req->vif_idx = vif->vif_index; + memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN); + + if (p_nhop_addr) + memcpy(&req->nhop_mac_addr, p_nhop_addr, ETH_ALEN); + + /* Send the MESH_PATH_UPDATE_REQ message to UMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MESH_PATH_UPDATE_CFM, cfm); +} + +void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + u8 *ext_addr) +{ + // Message to send + struct mesh_proxy_add_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MESH_PROXY_ADD_REQ message */ + req = rwnx_msg_zalloc(MESH_PROXY_ADD_REQ, TASK_MESH, DRV_TASK_ID, + sizeof(struct mesh_proxy_add_req)); + + if (req) { + req->vif_idx = vif->vif_index; + memcpy(&req->ext_sta_addr, ext_addr, ETH_ALEN); + + /* Send the MESH_PROXY_ADD_REQ message to UMAC FW */ + rwnx_send_msg(rwnx_hw, req, 0, 0, NULL); + } +} + +int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif) +{ + struct tdls_peer_traffic_ind_req *tdls_peer_traffic_ind_req; + + if (!rwnx_vif->sta.tdls_sta) + return -ENOLINK; + + /* Build the TDLS_PEER_TRAFFIC_IND_REQ message */ + tdls_peer_traffic_ind_req = + rwnx_msg_zalloc(TDLS_PEER_TRAFFIC_IND_REQ, TASK_TDLS, DRV_TASK_ID, + sizeof(struct tdls_peer_traffic_ind_req)); + + if (!tdls_peer_traffic_ind_req) + return -ENOMEM; + + /* Set parameters for the TDLS_PEER_TRAFFIC_IND_REQ message */ + tdls_peer_traffic_ind_req->vif_index = rwnx_vif->vif_index; + tdls_peer_traffic_ind_req->sta_idx = rwnx_vif->sta.tdls_sta->sta_idx; + memcpy(&tdls_peer_traffic_ind_req->peer_mac_addr.array[0], + rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN); + tdls_peer_traffic_ind_req->dialog_token = 0; // check dialog token value + tdls_peer_traffic_ind_req->last_tid = rwnx_vif->sta.tdls_sta->tdls.last_tid; + tdls_peer_traffic_ind_req->last_sn = rwnx_vif->sta.tdls_sta->tdls.last_sn; + + /* Send the TDLS_PEER_TRAFFIC_IND_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, tdls_peer_traffic_ind_req, 0, 0, NULL); +} + +int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw, + struct cfg80211_chan_def *chandef, + struct me_config_monitor_cfm *cfm) +{ + struct me_config_monitor_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the ME_CONFIG_MONITOR_REQ message */ + req = rwnx_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID, + sizeof(struct me_config_monitor_req)); + if (!req) + return -ENOMEM; + + if (chandef) { + req->chan_set = true; + + req->chan.band = chandef->chan->band; + req->chan.type = bw2chnl[chandef->width]; + req->chan.prim20_freq = chandef->chan->center_freq; + req->chan.center1_freq = chandef->center_freq1; + req->chan.center2_freq = chandef->center_freq2; + req->chan.tx_power = chan_to_fw_pwr(chandef->chan->max_power); + + if (rwnx_hw->phy.limit_bw) + limit_chan_bw(&req->chan.type, req->chan.prim20_freq, + &req->chan.center1_freq); + } else { + req->chan_set = false; + } + + req->uf = rwnx_hw->mod_params->uf; + req->auto_reply = rwnx_hw->mod_params->auto_reply; + + /* Send the ME_CONFIG_MONITOR_REQ message to FW */ + return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_MONITOR_CFM, cfm); +} +#endif /* CONFIG_RWNX_FULLMAC */ + +int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, + struct rwnx_sta *rwnx_sta, + bool sta_initiator, u8 oper_class, + struct cfg80211_chan_def *chandef, + struct tdls_chan_switch_cfm *cfm) +{ + struct tdls_chan_switch_req *tdls_chan_switch_req; + + /* Build the TDLS_CHAN_SWITCH_REQ message */ + tdls_chan_switch_req = + rwnx_msg_zalloc(TDLS_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID, + sizeof(struct tdls_chan_switch_req)); + + if (!tdls_chan_switch_req) + return -ENOMEM; + + /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */ + tdls_chan_switch_req->vif_index = rwnx_vif->vif_index; + tdls_chan_switch_req->sta_idx = rwnx_sta->sta_idx; + memcpy(&tdls_chan_switch_req->peer_mac_addr.array[0], + rwnx_sta_addr(rwnx_sta), ETH_ALEN); + tdls_chan_switch_req->initiator = sta_initiator; + tdls_chan_switch_req->band = chandef->chan->band; + tdls_chan_switch_req->type = bw2chnl[chandef->width]; + tdls_chan_switch_req->prim20_freq = chandef->chan->center_freq; + tdls_chan_switch_req->center1_freq = chandef->center_freq1; + tdls_chan_switch_req->center2_freq = chandef->center_freq2; + tdls_chan_switch_req->tx_power = chan_to_fw_pwr(chandef->chan->max_power); + tdls_chan_switch_req->op_class = oper_class; + + /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, tdls_chan_switch_req, 1, TDLS_CHAN_SWITCH_CFM, + cfm); +} + +int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, + struct rwnx_sta *rwnx_sta, + struct tdls_cancel_chan_switch_cfm *cfm) +{ + struct tdls_cancel_chan_switch_req *tdls_cancel_chan_switch_req; + + /* Build the TDLS_CHAN_SWITCH_REQ message */ + tdls_cancel_chan_switch_req = + rwnx_msg_zalloc(TDLS_CANCEL_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID, + sizeof(struct tdls_cancel_chan_switch_req)); + if (!tdls_cancel_chan_switch_req) + return -ENOMEM; + + /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */ + tdls_cancel_chan_switch_req->vif_index = rwnx_vif->vif_index; + tdls_cancel_chan_switch_req->sta_idx = rwnx_sta->sta_idx; + memcpy(&tdls_cancel_chan_switch_req->peer_mac_addr.array[0], + rwnx_sta_addr(rwnx_sta), ETH_ALEN); + + /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, tdls_cancel_chan_switch_req, 1, + TDLS_CANCEL_CHAN_SWITCH_CFM, cfm); +} + +#ifdef CONFIG_RWNX_BFMER +#ifdef CONFIG_RWNX_FULLMAC +void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta, + const struct ieee80211_vht_cap *vht_cap) +#endif /* CONFIG_RWNX_FULLMAC */ +{ + struct mm_bfmer_enable_req *bfmer_en_req; +#ifdef CONFIG_RWNX_FULLMAC + u32 vht_capability; + u8 rx_nss = 0; +#endif /* CONFIG_RWNX_FULLMAC */ + + RWNX_DBG(RWNX_FN_ENTRY_STR); + +#ifdef CONFIG_RWNX_FULLMAC + if (!vht_cap) { +#endif /* CONFIG_RWNX_FULLMAC */ + goto end; + } + +#ifdef CONFIG_RWNX_FULLMAC + vht_capability = le32_to_cpu(vht_cap->vht_cap_info); +#endif /* CONFIG_RWNX_FULLMAC */ + + if (!(vht_capability & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + goto end; + +#ifdef CONFIG_RWNX_FULLMAC + rx_nss = rwnx_bfmer_get_rx_nss(vht_cap); +#endif /* CONFIG_RWNX_FULLMAC */ + + /* Allocate a structure that will contain the beamforming report */ + if (rwnx_bfmer_report_add(rwnx_hw, rwnx_sta, + RWNX_BFMER_REPORT_SPACE_SIZE)) + goto end; + + /* Build the MM_BFMER_ENABLE_REQ message */ + bfmer_en_req = rwnx_msg_zalloc(MM_BFMER_ENABLE_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_bfmer_enable_req)); + + /* Check message allocation */ + if (!bfmer_en_req) { + /* Free memory allocated for the report */ + rwnx_bfmer_report_del(rwnx_hw, rwnx_sta); + /* Do not use beamforming */ + goto end; + } + + /* Provide DMA address to the MAC */ + bfmer_en_req->host_bfr_addr = rwnx_sta->bfm_report->dma_addr; + bfmer_en_req->host_bfr_size = RWNX_BFMER_REPORT_SPACE_SIZE; + bfmer_en_req->sta_idx = rwnx_sta->sta_idx; +#ifdef CONFIG_RWNX_FULLMAC + bfmer_en_req->aid = rwnx_sta->aid; + bfmer_en_req->rx_nss = rx_nss; +#endif /* CONFIG_RWNX_FULLMAC */ + + if (vht_capability & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) + bfmer_en_req->vht_mu_bfmee = true; + else + bfmer_en_req->vht_mu_bfmee = false; + + /* Send the MM_BFMER_EN_REQ message to LMAC FW */ + rwnx_send_msg(rwnx_hw, bfmer_en_req, 0, 0, NULL); + +end: + return; +} + +#ifdef CONFIG_RWNX_MUMIMO_TX +int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw, + struct rwnx_sta *rwnx_sta) +{ + struct mm_mu_group_update_req *req; + int group_id, i = 0; + u64 map; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_MU_GROUP_UPDATE_REQ message */ + req = + rwnx_msg_zalloc(MM_MU_GROUP_UPDATE_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_mu_group_update_req) + + rwnx_sta->group_info.cnt * sizeof(req->groups[0])); + + /* Check message allocation */ + if (!req) + return -ENOMEM; + + map = rwnx_sta->group_info.map & RWNX_MU_GROUP_MASK; + for (group_id = (fls64(map) - 1); group_id > 0; + map &= ~(u64)BIT_ULL(group_id), group_id = (fls64(map) - 1)) { + int user_pos = rwnx_mu_group_sta_get_pos(rwnx_hw, rwnx_sta, group_id); + + if (WARN(i >= rwnx_sta->group_info.cnt, + "STA%d: Too much group (%d)\n", rwnx_sta->sta_idx, i + 1)) + break; + + req->groups[i].group_id = group_id; + req->groups[i].user_pos = user_pos; + + i++; + } + + req->group_cnt = rwnx_sta->group_info.cnt; + req->sta_idx = rwnx_sta->sta_idx; + + /* Send the MM_MU_GROUP_UPDATE_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_MU_GROUP_UPDATE_CFM, NULL); +} +#endif /* CONFIG_RWNX_MUMIMO_TX */ +#endif /* CONFIG_RWNX_BFMER */ + +/********************************************************************** + * Debug Messages + *********************************************************************/ +int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg) +{ + struct mm_dbg_trigger_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_DBG_TRIGGER_REQ message */ + req = rwnx_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_dbg_trigger_req)); + if (!req) + return -ENOMEM; + + /* Set parameters for the MM_DBG_TRIGGER_REQ message */ + strscpy(req->error, msg, sizeof(req->error)); + + /* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 0, -1, NULL); +} + +int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + struct dbg_mem_read_cfm *cfm) +{ + struct dbg_mem_read_req *mem_read_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_MEM_READ_REQ message */ + mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_read_req)); + if (!mem_read_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_READ_REQ message */ + mem_read_req->memaddr = mem_addr; + + /* Send the DBG_MEM_READ_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm); +} + +int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + u32 mem_data) +{ + struct dbg_mem_write_req *mem_write_req; + + // RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_MEM_WRITE_REQ message */ + mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_write_req)); + if (!mem_write_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_WRITE_REQ message */ + mem_write_req->memaddr = mem_addr; + mem_write_req->memdata = mem_data; + + /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL); +} + +int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + u32 mem_mask, u32 mem_data) +{ + struct dbg_mem_mask_write_req *mem_mask_write_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_MEM_MASK_WRITE_REQ message */ + mem_mask_write_req = + rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_mask_write_req)); + if (!mem_mask_write_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */ + mem_mask_write_req->memaddr = mem_addr; + mem_mask_write_req->memmask = mem_mask; + mem_mask_write_req->memdata = mem_data; + + /* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM, + NULL); +} + +int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32 cmd, u32 argc, + u8 *argv, struct dbg_rftest_cmd_cfm *cfm) +{ + struct dbg_rftest_cmd_req *mem_rftest_cmd_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_RFTEST_CMD_REQ message */ + mem_rftest_cmd_req = + rwnx_msg_zalloc(DBG_RFTEST_CMD_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_rftest_cmd_req)); + if (!mem_rftest_cmd_req) + return -ENOMEM; + + if (argc > 10) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */ + mem_rftest_cmd_req->cmd = cmd; + mem_rftest_cmd_req->argc = argc; + if (argc != 0) + memcpy(mem_rftest_cmd_req->argv, argv, argc); + + /* Send the DBG_RFTEST_CMD_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, mem_rftest_cmd_req, 1, DBG_RFTEST_CMD_CFM, + cfm); +} + +int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter) +{ + struct dbg_set_mod_filter_req *set_mod_filter_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_SET_MOD_FILTER_REQ message */ + set_mod_filter_req = + rwnx_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_set_mod_filter_req)); + if (!set_mod_filter_req) + return -ENOMEM; + + /* Set parameters for the DBG_SET_MOD_FILTER_REQ message */ + set_mod_filter_req->mod_filter = filter; + + /* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, + NULL); +} + +int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter) +{ + struct dbg_set_sev_filter_req *set_sev_filter_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_SET_SEV_FILTER_REQ message */ + set_sev_filter_req = + rwnx_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_set_sev_filter_req)); + if (!set_sev_filter_req) + return -ENOMEM; + + /* Set parameters for the DBG_SET_SEV_FILTER_REQ message */ + set_sev_filter_req->sev_filter = filter; + + /* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, + NULL); +} + +int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw, + struct dbg_get_sys_stat_cfm *cfm) +{ + void *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Allocate the message */ + req = rwnx_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0); + if (!req) + return -ENOMEM; + + /* Send the DBG_MEM_READ_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm); +} + +int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + u32 mem_size, u32 *mem_data) +{ + struct dbg_mem_block_write_req *mem_blk_write_req; + + // RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_MEM_BLOCK_WRITE_REQ message */ + mem_blk_write_req = + rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_mem_block_write_req)); + if (!mem_blk_write_req) + return -ENOMEM; + + /* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */ + mem_blk_write_req->memaddr = mem_addr; + mem_blk_write_req->memsize = mem_size; + memcpy(mem_blk_write_req->memdata, mem_data, mem_size); + + /* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM, + NULL); +} + +int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr, + u32 boot_type) +{ + struct dbg_start_app_req *start_app_req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the DBG_START_APP_REQ message */ + start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID, + sizeof(struct dbg_start_app_req)); + if (!start_app_req) + return -ENOMEM; + + /* Set parameters for the DBG_START_APP_REQ message */ + start_app_req->bootaddr = boot_addr; + start_app_req->boottype = boot_type; + + /* Send the DBG_START_APP_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, start_app_req, 1, DBG_START_APP_CFM, NULL); +} + +int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index, + int rssi_thold, u32 rssi_hyst) +{ + struct mm_cfg_rssi_req *req; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + /* Build the MM_CFG_RSSI_REQ message */ + req = rwnx_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID, + sizeof(struct mm_cfg_rssi_req)); + if (!req) + return -ENOMEM; + + if (!rwnx_hw->vif_table[vif_index]) + return 0; + + /* Set parameters for the MM_CFG_RSSI_REQ message */ + req->vif_index = vif_index; + req->rssi_thold = (s8)rssi_thold; + req->rssi_hyst = (u8)rssi_hyst; + + /* Send the MM_CFG_RSSI_REQ message to LMAC FW */ + return rwnx_send_msg(rwnx_hw, req, 1, MM_CFG_RSSI_CFM, NULL); +} diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h new file mode 100644 index 0000000000000..1e01ef8f7976c --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_msg_tx.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RWNX_MSG_TX_H_ +#define _RWNX_MSG_TX_H_ + +#include "lmac_types.h" +#include "rwnx_defs.h" + +int rwnx_send_reset(struct rwnx_hw *rwnx_hw); + +int rwnx_send_start(struct rwnx_hw *rwnx_hw); + +int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm); + +int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac, + enum nl80211_iftype iftype, bool p2p, + struct mm_add_if_cfm *cfm); + +int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer); + +int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx, + struct mm_set_channel_cfm *cfm); + +int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx, + bool pairwise, u8 *key, u8 key_len, u8 key_idx, + u8 cipher_suite, struct mm_key_add_cfm *cfm); + +int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx); + +int rwnx_send_bcn(struct rwnx_hw *rwnx_hw, u8 *buf, u8 vif_idx, u16 bcn_len); + +int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr, + u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft); + +int rwnx_send_tim_update(struct rwnx_hw *rwnx_hw, u8 vif_idx, u16 aid, + u8 tx_status); + +int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct ieee80211_channel *chan, unsigned int duration, + struct mm_remain_on_channel_cfm *roc_cfm); + +int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw); + +int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr, + struct mm_set_power_cfm *cfm); + +int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param, + bool uapsd, u8 inst_nbr); + +int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, + struct rwnx_sta *rwnx_sta, + bool sta_initiator, u8 oper_class, + struct cfg80211_chan_def *chandef, + struct tdls_chan_switch_cfm *cfm); + +int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, + struct rwnx_sta *rwnx_sta, + struct tdls_cancel_chan_switch_cfm *cfm); + +#ifdef AICWF_ARP_OFFLOAD +int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, u32 ipaddr, + u8 enable); +#endif +int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8 ofst, u8 sel, u8 *tbl, u16 len); + +int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw, + struct mm_set_rf_calib_cfm *cfm); + +int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw, + struct mm_get_mac_addr_cfm *cfm); + +#ifdef CONFIG_RWNX_FULLMAC +int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw); + +int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw); + +int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened, + u8 sta_idx); + +int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw, + struct station_parameters *params, const u8 *mac, + u8 inst_nbr, struct me_sta_add_cfm *cfm); + +int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta); + +int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd, + u8 tx_status); + +int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw, u8 sta_idx, + struct me_rc_stats_cfm *cfm); + +int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw, u8 sta_idx, u16 rate_idx); + +int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode); + +int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level, u8 disable_filter); + +int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, + struct cfg80211_connect_params *sme, struct sm_connect_cfm *cfm); + +int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, u16 reason); + +int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif, u16 status); + +int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct cfg80211_ap_settings *settings, struct apm_start_cfm *cfm, + struct rwnx_ipc_elem_var *elem); + +int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif); + +int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, + struct cfg80211_scan_request *param); + +int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw, + struct scan_cancel_cfm *cfm); + +int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct cfg80211_chan_def *chandef, + struct apm_start_cac_cfm *cfm); + +int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif); + +int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *rwnx_vif); + +int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw, + struct cfg80211_chan_def *chandef, + struct me_config_monitor_cfm *cfm); + +int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + const struct mesh_config *conf, const struct mesh_setup *setup, + struct mesh_start_cfm *cfm); + +int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + struct mesh_stop_cfm *cfm); + +int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + u32 mask, const struct mesh_config *p_mconf, + struct mesh_update_cfm *cfm); + +int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + u8 sta_idx, struct mesh_peer_info_cfm *cfm); + +void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *vif, u8 sta_idx, + u8 mlink_state); + +void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *vif, u8 *tgt_addr); + +int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw, + struct rwnx_vif *vif, const u8 *tgt_addr, + const u8 *p_nhop_addr, + struct mesh_path_update_cfm *cfm); + +void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, + u8 *ext_addr); + +#endif /* CONFIG_RWNX_FULLMAC */ + +#ifdef CONFIG_RWNX_BFMER +#ifdef CONFIG_RWNX_FULLMAC +void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta, + const struct ieee80211_vht_cap *vht_cap); + +#endif /* CONFIG_RWNX_FULLMAC */ +#ifdef CONFIG_RWNX_MUMIMO_TX +int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw, + struct rwnx_sta *rwnx_sta); +#endif /* CONFIG_RWNX_MUMIMO_TX */ +#endif /* CONFIG_RWNX_BFMER */ + +/* Debug messages */ +int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg); + +int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + struct dbg_mem_read_cfm *cfm); + +int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + u32 mem_data); + +int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + u32 mem_mask, u32 mem_data); + +int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter); + +int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32 cmd, u32 argc, + u8 *argv, struct dbg_rftest_cmd_cfm *cfm); + +int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter); + +int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw, + struct dbg_get_sys_stat_cfm *cfm); + +int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr, + u32 mem_size, u32 *mem_data); + +int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr, + u32 boot_type); + +int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index, + int rssi_thold, u32 rssi_hyst); + +int rwnx_send_disable_agg_req(struct rwnx_hw *rwnx_hw, u8 agg_disable, + u8 agg_disable_rx, u8 sta_idx); + +int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8 disable_coexnull, + u8 enable_nullcts); + +int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8 sta_idx, + struct mm_get_sta_info_cfm *cfm); + +int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8 on, + u8 efuse_valid, u8 set_vendor_info, + u8 fwtrace_redir_en, + struct mm_set_stack_start_cfm *cfm); + +int rwnx_send_get_temp_req(struct rwnx_hw *rwnx_hw, + struct mm_set_vendor_swconfig_cfm *cfm); + +int rwnx_send_get_temp_hwconfig_req(struct rwnx_hw *rwnx_hw, + struct mm_set_vendor_hwconfig_cfm *cfm); + +int rwnx_send_set_temp_comp_req(struct rwnx_hw *rwnx_hw, + struct mm_set_vendor_swconfig_cfm *cfm); + +int rwnx_send_txop_req(struct rwnx_hw *rwnx_hw, uint16_t *txop, + u8 long_nav_en, u8 cfe_en); + +int rwnx_send_vendor_hwconfig_req(struct rwnx_hw *rwnx_hw, u32 hwconfig_id, + s32 *param, s32 *param_out); + +int rwnx_send_vendor_swconfig_req(struct rwnx_hw *rwnx_hw, u32 swconfig_id, + s32 *param_in, s32 *param_out); + +int rwnx_send_mask_set_ext_flags_req(struct rwnx_hw *rwnx_hw, + u32 flags_mask, u32 flags_val, + struct mm_set_vendor_swconfig_cfm *cfm); + +int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, + struct mm_get_fw_version_cfm *cfm); + +int rwnx_send_txpwr_idx_req(struct rwnx_hw *rwnx_hw); + +int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw); + +int rwnx_send_txpwr_ofst2x_req(struct rwnx_hw *rwnx_hw); + +int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw); + +int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw, int index_reg); + +int rwnx_send_txpwr_lvl_adj_req(struct rwnx_hw *rwnx_hw); + +struct rwnx_cmd *rwnx_cmd_malloc(void); + +void rwnx_cmd_free(struct rwnx_cmd *cmd); + +int rwnx_init_cmd_array(void); + +void rwnx_free_cmd_array(void); + +#endif /* _RWNX_MSG_TX_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c new file mode 100644 index 0000000000000..a4d96a6a082f5 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.c @@ -0,0 +1,2868 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + ****************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_platform.c + * + * @brief Platform profile + * + ****************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hal_desc.h" +#include "reg_access.h" +#include "rwnx_main.h" +#include "rwnx_platform.h" +#include "ipc_host.h" +#include "rwnx_msg_tx.h" + +#ifdef AICWF_SDIO_SUPPORT +#include "aicwf_sdio.h" +#endif + +#include "aicwf_compat_8800d80.h" +#include "aicwf_compat_8800dc.h" +#include "aicwf_chip_ops.h" + +// Parser state +#define INIT 0 +#define CMD 1 +#define PRINT 2 +#define GET_VALUE 3 + +struct reg_table { + char ccode[3]; + enum regions_code region; +}; + +/* If the region conflicts with the kernel, the actual authentication standard prevails */ +static struct reg_table reg_tables[] = { + {.ccode = "CN", .region = REGIONS_SRRC}, + {.ccode = "US", .region = REGIONS_FCC}, + {.ccode = "DE", .region = REGIONS_ETSI}, + {.ccode = "00", .region = REGIONS_DEFAULT}, + {.ccode = "WW", .region = REGIONS_DEFAULT}, + {.ccode = "XX", .region = REGIONS_DEFAULT}, + {.ccode = "JP", .region = REGIONS_JP}, + {.ccode = "AD", .region = REGIONS_ETSI}, + {.ccode = "AE", .region = REGIONS_ETSI}, + {.ccode = "AF", .region = REGIONS_ETSI}, + {.ccode = "AI", .region = REGIONS_ETSI}, + {.ccode = "AL", .region = REGIONS_ETSI}, + {.ccode = "AM", .region = REGIONS_ETSI}, + {.ccode = "AN", .region = REGIONS_ETSI}, + {.ccode = "AR", .region = REGIONS_FCC}, + {.ccode = "AS", .region = REGIONS_FCC}, + {.ccode = "AT", .region = REGIONS_ETSI}, + {.ccode = "AU", .region = REGIONS_ETSI}, + {.ccode = "AW", .region = REGIONS_ETSI}, + {.ccode = "AZ", .region = REGIONS_ETSI}, + {.ccode = "BA", .region = REGIONS_ETSI}, + {.ccode = "BB", .region = REGIONS_FCC}, + {.ccode = "BD", .region = REGIONS_ETSI}, + {.ccode = "BE", .region = REGIONS_ETSI}, + {.ccode = "BF", .region = REGIONS_FCC}, + {.ccode = "BG", .region = REGIONS_ETSI}, + {.ccode = "BH", .region = REGIONS_ETSI}, + {.ccode = "BL", .region = REGIONS_ETSI}, + {.ccode = "BM", .region = REGIONS_FCC}, + {.ccode = "BN", .region = REGIONS_JP}, + {.ccode = "BO", .region = REGIONS_JP}, + {.ccode = "BR", .region = REGIONS_FCC}, + {.ccode = "BS", .region = REGIONS_FCC}, + {.ccode = "BT", .region = REGIONS_ETSI}, + {.ccode = "BW", .region = REGIONS_ETSI}, + {.ccode = "BY", .region = REGIONS_ETSI}, + {.ccode = "BZ", .region = REGIONS_JP}, + {.ccode = "CA", .region = REGIONS_FCC}, + {.ccode = "CF", .region = REGIONS_FCC}, + {.ccode = "CH", .region = REGIONS_ETSI}, + {.ccode = "CI", .region = REGIONS_FCC}, + {.ccode = "CL", .region = REGIONS_ETSI}, + {.ccode = "CO", .region = REGIONS_FCC}, + {.ccode = "CR", .region = REGIONS_FCC}, + {.ccode = "CU", .region = REGIONS_FCC}, + {.ccode = "CX", .region = REGIONS_FCC}, + {.ccode = "CY", .region = REGIONS_ETSI}, + {.ccode = "CZ", .region = REGIONS_ETSI}, + {.ccode = "DK", .region = REGIONS_ETSI}, + {.ccode = "DM", .region = REGIONS_FCC}, + {.ccode = "DO", .region = REGIONS_FCC}, + {.ccode = "DZ", .region = REGIONS_ETSI}, + {.ccode = "EC", .region = REGIONS_FCC}, + {.ccode = "EE", .region = REGIONS_ETSI}, + {.ccode = "EG", .region = REGIONS_ETSI}, + {.ccode = "ES", .region = REGIONS_ETSI}, + {.ccode = "ET", .region = REGIONS_ETSI}, + {.ccode = "FI", .region = REGIONS_ETSI}, + {.ccode = "FK", .region = REGIONS_ETSI}, + {.ccode = "FM", .region = REGIONS_FCC}, + {.ccode = "FO", .region = REGIONS_ETSI}, + {.ccode = "FR", .region = REGIONS_ETSI}, + {.ccode = "GB", .region = REGIONS_ETSI}, + {.ccode = "GD", .region = REGIONS_FCC}, + {.ccode = "GE", .region = REGIONS_ETSI}, + {.ccode = "GF", .region = REGIONS_ETSI}, + {.ccode = "GH", .region = REGIONS_ETSI}, + {.ccode = "GI", .region = REGIONS_ETSI}, + {.ccode = "GL", .region = REGIONS_ETSI}, + {.ccode = "GP", .region = REGIONS_ETSI}, + {.ccode = "GR", .region = REGIONS_ETSI}, + {.ccode = "GT", .region = REGIONS_FCC}, + {.ccode = "GU", .region = REGIONS_FCC}, + {.ccode = "GY", .region = REGIONS_DEFAULT}, + {.ccode = "HK", .region = REGIONS_ETSI}, + {.ccode = "HN", .region = REGIONS_FCC}, + {.ccode = "HR", .region = REGIONS_ETSI}, + {.ccode = "HT", .region = REGIONS_FCC}, + {.ccode = "HU", .region = REGIONS_ETSI}, + {.ccode = "ID", .region = REGIONS_ETSI}, + {.ccode = "IE", .region = REGIONS_ETSI}, + {.ccode = "IL", .region = REGIONS_ETSI}, + {.ccode = "IN", .region = REGIONS_ETSI}, + {.ccode = "IQ", .region = REGIONS_ETSI}, + {.ccode = "IR", .region = REGIONS_JP}, + {.ccode = "IS", .region = REGIONS_ETSI}, + {.ccode = "IT", .region = REGIONS_ETSI}, + {.ccode = "JM", .region = REGIONS_FCC}, + {.ccode = "JO", .region = REGIONS_ETSI}, + {.ccode = "KE", .region = REGIONS_ETSI}, + {.ccode = "KG", .region = REGIONS_ETSI}, + {.ccode = "KH", .region = REGIONS_ETSI}, + {.ccode = "KN", .region = REGIONS_ETSI}, + {.ccode = "KP", .region = REGIONS_JP}, + {.ccode = "KR", .region = REGIONS_ETSI}, + {.ccode = "KW", .region = REGIONS_ETSI}, + {.ccode = "KY", .region = REGIONS_FCC}, + {.ccode = "KZ", .region = REGIONS_ETSI}, + {.ccode = "LB", .region = REGIONS_ETSI}, + {.ccode = "LC", .region = REGIONS_ETSI}, + {.ccode = "LI", .region = REGIONS_ETSI}, + {.ccode = "LK", .region = REGIONS_FCC}, + {.ccode = "LS", .region = REGIONS_ETSI}, + {.ccode = "LT", .region = REGIONS_ETSI}, + {.ccode = "LU", .region = REGIONS_ETSI}, + {.ccode = "LV", .region = REGIONS_ETSI}, + {.ccode = "LY", .region = REGIONS_ETSI}, + {.ccode = "MA", .region = REGIONS_ETSI}, + {.ccode = "MC", .region = REGIONS_ETSI}, + {.ccode = "MD", .region = REGIONS_ETSI}, + {.ccode = "ME", .region = REGIONS_ETSI}, + {.ccode = "MF", .region = REGIONS_ETSI}, + {.ccode = "MH", .region = REGIONS_FCC}, + {.ccode = "MK", .region = REGIONS_ETSI}, + {.ccode = "MN", .region = REGIONS_ETSI}, + {.ccode = "MO", .region = REGIONS_ETSI}, + {.ccode = "MP", .region = REGIONS_FCC}, + {.ccode = "MQ", .region = REGIONS_ETSI}, + {.ccode = "MR", .region = REGIONS_ETSI}, + {.ccode = "MT", .region = REGIONS_ETSI}, + {.ccode = "MU", .region = REGIONS_FCC}, + {.ccode = "MV", .region = REGIONS_ETSI}, + {.ccode = "MW", .region = REGIONS_ETSI}, + {.ccode = "MX", .region = REGIONS_FCC}, + {.ccode = "MY", .region = REGIONS_ETSI}, + {.ccode = "NA", .region = REGIONS_ETSI}, + {.ccode = "NG", .region = REGIONS_ETSI}, + {.ccode = "NI", .region = REGIONS_FCC}, + {.ccode = "NL", .region = REGIONS_ETSI}, + {.ccode = "NO", .region = REGIONS_ETSI}, + {.ccode = "NP", .region = REGIONS_JP}, + {.ccode = "NZ", .region = REGIONS_ETSI}, + {.ccode = "OM", .region = REGIONS_ETSI}, + {.ccode = "PA", .region = REGIONS_FCC}, + {.ccode = "PE", .region = REGIONS_ETSI}, + {.ccode = "PF", .region = REGIONS_ETSI}, + {.ccode = "PG", .region = REGIONS_FCC}, + {.ccode = "PH", .region = REGIONS_ETSI}, + {.ccode = "PK", .region = REGIONS_ETSI}, + {.ccode = "PL", .region = REGIONS_ETSI}, + {.ccode = "PM", .region = REGIONS_ETSI}, + {.ccode = "PR", .region = REGIONS_FCC}, + {.ccode = "PT", .region = REGIONS_ETSI}, + {.ccode = "PW", .region = REGIONS_FCC}, + {.ccode = "PY", .region = REGIONS_FCC}, + {.ccode = "QA", .region = REGIONS_ETSI}, + {.ccode = "RE", .region = REGIONS_ETSI}, + {.ccode = "RO", .region = REGIONS_ETSI}, + {.ccode = "RS", .region = REGIONS_ETSI}, + {.ccode = "RU", .region = REGIONS_ETSI}, + {.ccode = "RW", .region = REGIONS_FCC}, + {.ccode = "SA", .region = REGIONS_ETSI}, + {.ccode = "SE", .region = REGIONS_ETSI}, + {.ccode = "SG", .region = REGIONS_ETSI}, + {.ccode = "SI", .region = REGIONS_ETSI}, + {.ccode = "SK", .region = REGIONS_ETSI}, + {.ccode = "SM", .region = REGIONS_ETSI}, + {.ccode = "SN", .region = REGIONS_FCC}, + {.ccode = "SR", .region = REGIONS_ETSI}, + {.ccode = "SV", .region = REGIONS_FCC}, + {.ccode = "SY", .region = REGIONS_DEFAULT}, + {.ccode = "TC", .region = REGIONS_FCC}, + {.ccode = "TD", .region = REGIONS_ETSI}, + {.ccode = "TG", .region = REGIONS_ETSI}, + {.ccode = "TH", .region = REGIONS_ETSI}, + {.ccode = "TJ", .region = REGIONS_ETSI}, + {.ccode = "TM", .region = REGIONS_ETSI}, + {.ccode = "TN", .region = REGIONS_ETSI}, + {.ccode = "TR", .region = REGIONS_ETSI}, + {.ccode = "TT", .region = REGIONS_FCC}, + {.ccode = "TW", .region = REGIONS_FCC}, + {.ccode = "TZ", .region = REGIONS_DEFAULT}, + {.ccode = "UA", .region = REGIONS_ETSI}, + {.ccode = "UG", .region = REGIONS_FCC}, + {.ccode = "UY", .region = REGIONS_ETSI}, + {.ccode = "UZ", .region = REGIONS_ETSI}, + {.ccode = "VC", .region = REGIONS_ETSI}, + {.ccode = "VE", .region = REGIONS_FCC}, + {.ccode = "VI", .region = REGIONS_FCC}, + {.ccode = "VN", .region = REGIONS_JP}, + {.ccode = "VU", .region = REGIONS_FCC}, + {.ccode = "WF", .region = REGIONS_ETSI}, + {.ccode = "WS", .region = REGIONS_ETSI}, + {.ccode = "YE", .region = REGIONS_DEFAULT}, + {.ccode = "YT", .region = REGIONS_ETSI}, + {.ccode = "ZA", .region = REGIONS_ETSI}, + {.ccode = "ZM", .region = REGIONS_ETSI}, + {.ccode = "ZW", .region = REGIONS_ETSI}, + {.ccode = "XK", .region = REGIONS_ETSI}, +}; + +struct rwnx_plat *g_rwnx_plat; + +struct userconfig_info_t { + struct txpwr_lvl_conf txpwr_lvl; + struct txpwr_lvl_conf_v2 txpwr_lvl_v2; + struct txpwr_lvl_conf_v3 txpwr_lvl_v3; + struct txpwr_lvl_conf_v3 txpwr_lvl_v3_gp[REGION_NUM_MAX]; + struct txpwr_lvl_adj_conf txpwr_lvl_adj; + struct txpwr_loss_conf txpwr_loss; + struct txpwr_ofst_conf txpwr_ofst; + struct txpwr_ofst2x_conf txpwr_ofst2x; + struct xtal_cap_conf xtal_cap; +}; + +static struct userconfig_info_t userconfig_info = { + .txpwr_lvl = { .enable = 1, + .dsss = 9, + .ofdmlowrate_2g4 = 8, + .ofdm64qam_2g4 = 8, + .ofdm256qam_2g4 = 8, + .ofdm1024qam_2g4 = 8, + .ofdmlowrate_5g = 11, + .ofdm64qam_5g = 10, + .ofdm256qam_5g = 9, + .ofdm1024qam_5g = 9}, + .txpwr_lvl_v2 = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = + // 1M, 2M, 5M5, 11M, 6M, 9M, 12M, 18M, 24M, 36M, + // 48M, 54M + {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, + // MCS10,MCS11 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + }, + .txpwr_lvl_v3 = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = + // 1M, 2M, 5M5, 11M, 6M, 9M, 12M, 18M, 24M, 36M, + // 48M, 54M + {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, + // MCS10,MCS11 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + .pwrlvl_11a_5g = + // NA, NA, NA, NA, 6M, 9M, 12M, 18M, 24M, 36M, + // 48M, 54M + {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_5g = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 15}, + .pwrlvl_11ax_5g = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, + // MCS10,MCS11 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14}, + }, + .txpwr_lvl_v3_gp[0] = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = + // 1M, 2M, 5M5, 11M, 6M, 9M, 12M, 18M, 24M, 36M, + // 48M, 54M + {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, + // MCS10,MCS11 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + .pwrlvl_11a_5g = + // NA, NA, NA, NA, 6M, 9M, 12M, 18M, 24M, 36M, + // 48M, 54M + {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_5g = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 15}, + .pwrlvl_11ax_5g = + // MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, + // MCS10,MCS11 + {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14}, + }, + .txpwr_lvl_v3_gp[1] = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + .pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15}, + .pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14}, + }, + .txpwr_lvl_v3_gp[2] = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + .pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15}, + .pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14}, + }, + .txpwr_lvl_v3_gp[3] = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + .pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15}, + .pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14}, + }, + .txpwr_lvl_v3_gp[4] = { .enable = 1, + .pwrlvl_11b_11ag_2g4 = {20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16}, + .pwrlvl_11ax_2g4 = {20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15}, + .pwrlvl_11a_5g = {0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16}, + .pwrlvl_11n_11ac_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15}, + .pwrlvl_11ax_5g = {20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14}, + }, + .txpwr_loss = { .loss_enable_2g4 = 1, + .loss_value_2g4 = 0, + .loss_enable_5g = 1, + .loss_value_5g = 0, + }, + .txpwr_ofst = { .enable = 1, + .chan_1_4 = 0, + .chan_5_9 = 0, + .chan_10_13 = 0, + .chan_36_64 = 0, + .chan_100_120 = 0, + .chan_122_140 = 0, + .chan_142_165 = 0, + }, + .txpwr_ofst2x = { .enable = 0, + .pwrofst2x_tbl_2g4 = { + // ch1-4, ch5-9, ch10-13 + {0, 0, 0}, // 11b + {0, 0, 0}, // ofdm_highrate + {0, 0, 0}, // ofdm_lowrate + }, + .pwrofst2x_tbl_5g = { + // ch42, ch58, ch106,ch122,ch138,ch155 + {0, 0, 0, 0, 0, 0}, // ofdm_lowrate + {0, 0, 0, 0, 0, 0}, // ofdm_highrate + {0, 0, 0, 0, 0, 0}, // ofdm_midrate + }, + }, + .xtal_cap = { .enable = 0, + .xtal_cap = 24, + .xtal_cap_fine = 31, + }, +}; + +#ifdef CONFIG_AIC8800_POWER_LIMIT +#define POWER_LIMIT_INVALID_VAL POWER_LEVEL_INVALID_VAL + +#define POWER_LIMIT_CC_MATCHED_BIT (0x1U << 0) + +struct txpwr_lmt_info_t { + u8 ch_cnt_2g4; + u8 ch_cnt_5g; + u8 ch_num_2g4[MAC_DOMAINCHANNEL_24G_MAX]; + u8 ch_num_5g[MAC_DOMAINCHANNEL_5G_MAX]; + s8 max_pwr_2g4[MAC_DOMAINCHANNEL_24G_MAX]; + s8 max_pwr_5g[MAC_DOMAINCHANNEL_5G_MAX]; +}; + +struct powerlimit_info_t { + u32 flags; + struct txpwr_lmt_info_t txpwr_lmt; +}; + +static struct powerlimit_info_t powerlimit_info = { + 0, +}; +#endif + +int aicwf_request_firmware(const struct firmware **fw, const char *name, + struct device *dev) +{ + char *fw_name; + int ret; + + if (!name || !name[0]) + return -EINVAL; + + fw_name = kasprintf(GFP_KERNEL, AIC8800_FW_DIR "%s", name); + if (!fw_name) + return -ENOMEM; + + ret = request_firmware(fw, fw_name, dev); + kfree(fw_name); + + return ret; +} + +#define MD5PINRT \ + "file " \ + "md5:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\r\n" + +static int rwnx_load_firmware(u32 **fw_buf, const char *name, + struct device *device) +{ + const struct firmware *fw = NULL; + u32 *dst = NULL; + void *buffer = NULL; + u8 digest[MD5_DIGEST_SIZE]; + int size = 0; + int ret = 0; + + AICWFDBG(LOGINFO, "AICWF %s: request firmware = %s\n", __func__, name); + + ret = aicwf_request_firmware(&fw, name, device); + + if (ret < 0) { + AICWFDBG(LOGDEBUG, "AICWF Load %s fail\n", name); + return ret; + } + + size = fw->size; + dst = (u32 *)fw->data; + + if (size <= 0) { + AICWFDBG(LOGDEBUG, "AICWF wrong size of firmware file\n"); + release_firmware(fw); + return -1; + } + + buffer = vmalloc(size); + if (!buffer) { + release_firmware(fw); + return -ENOMEM; + } + memset(buffer, 0, size); + memcpy(buffer, dst, size); + + *fw_buf = buffer; + + md5(buffer, size, digest); + AICWFDBG(LOGDEBUG, "AICWF " MD5PINRT, digest[0], digest[1], digest[2], digest[3], + digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], + digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); + + release_firmware(fw); + + return size; +} + +/* buffer is allocated by kzalloc */ +int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw, u32 **buffer, + const char *filename) +{ + int size; + + AICWFDBG(LOGDEBUG, "### Load file %s\n", filename); + + size = rwnx_load_firmware(buffer, filename, rwnx_hw->dev); + + return size; +} + +static void rwnx_restore_firmware(u32 **fw_buf) +{ + vfree(*fw_buf); + *fw_buf = NULL; +} + +void rwnx_release_firmware_common(u32 **buffer) +{ + rwnx_restore_firmware(buffer); +} + +/** + * rwnx_plat_bin_fw_upload_2 - Load the requested binary FW into embedded + * side. + * + * @rwnx_hw: Main driver data + * @fw_addr: Address where the fw must be loaded + * @filename: Name of the fw. + * + * Load a fw, stored as a binary file, into the specified address + * + * Return: 0 on success, or a negative error code. + */ +int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr, + char *filename) +{ + int err = 0; + unsigned int i = 0, size; + // u32 *src; + u32 *dst = NULL; + + /* Copy the file on the Embedded side */ + AICWFDBG(LOGINFO, "### Upload %s firmware, @ = %x\n", filename, fw_addr); + + size = rwnx_request_firmware_common(rwnx_hw, &dst, filename); + if (!dst) { + AICWFDBG(LOGERROR, "No such file or directory\n"); + return -1; + } + if (size <= 0) { + AICWFDBG(LOGERROR, "wrong size of firmware file\n"); + dst = NULL; + err = -1; + } + + AICWFDBG(LOGINFO, "size=%d, dst[0]=%x\n", size, dst[0]); + if (size > 512) { + for (; i < (size - 512); i += 512) { + // AICWFDBG(LOGDEBUG, "AICWF wr blk 0: %p -> %x\r\n", + // dst + i / 4, fw_addr + i); + err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, 512, + dst + i / 4); + if (err) { + AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n", + fw_addr + i, err); + break; + } + } + } + if (!err && i < size) { + // AICWFDBG(LOGDEBUG, "AICWF wr blk 1: %p -> %x\r\n", dst + i / 4, fw_addr + i); + err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, size - i, + dst + i / 4); + if (err) { + AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n", fw_addr + i, + err); + } + } + + if (dst) + rwnx_release_firmware_common(&dst); + + return err; +} + +struct nvram_info_t { + struct txpwr_idx_conf txpwr_idx; + struct txpwr_ofst_conf txpwr_ofst; + struct xtal_cap_conf xtal_cap; +}; + +static struct nvram_info_t nvram_info = { + .txpwr_idx = {.enable = 1, + .dsss = 9, + .ofdmlowrate_2g4 = 8, + .ofdm64qam_2g4 = 8, + .ofdm256qam_2g4 = 8, + .ofdm1024qam_2g4 = 8, + .ofdmlowrate_5g = 11, + .ofdm64qam_5g = 10, + .ofdm256qam_5g = 9, + .ofdm1024qam_5g = 9}, + .txpwr_ofst = { .enable = 1, + .chan_1_4 = 0, + .chan_5_9 = 0, + .chan_10_13 = 0, + .chan_36_64 = 0, + .chan_100_120 = 0, + .chan_122_140 = 0, + .chan_142_165 = 0, + }, + .xtal_cap = { .enable = 0, + .xtal_cap = 24, + .xtal_cap_fine = 31, + }, +}; + +void get_userconfig_txpwr_ofst_in_fdrv(struct txpwr_ofst_conf *txpwr_ofst) +{ + txpwr_ofst->enable = userconfig_info.txpwr_ofst.enable; + txpwr_ofst->chan_1_4 = userconfig_info.txpwr_ofst.chan_1_4; + txpwr_ofst->chan_5_9 = userconfig_info.txpwr_ofst.chan_5_9; + txpwr_ofst->chan_10_13 = userconfig_info.txpwr_ofst.chan_10_13; + txpwr_ofst->chan_36_64 = userconfig_info.txpwr_ofst.chan_36_64; + txpwr_ofst->chan_100_120 = userconfig_info.txpwr_ofst.chan_100_120; + txpwr_ofst->chan_122_140 = userconfig_info.txpwr_ofst.chan_122_140; + txpwr_ofst->chan_142_165 = userconfig_info.txpwr_ofst.chan_142_165; + + AICWFDBG(LOGINFO, "%s:enable :%d\r\n", __func__, txpwr_ofst->enable); + AICWFDBG(LOGINFO, "%s:chan_1_4 :%d\r\n", __func__, txpwr_ofst->chan_1_4); + AICWFDBG(LOGINFO, "%s:chan_5_9 :%d\r\n", __func__, txpwr_ofst->chan_5_9); + AICWFDBG(LOGINFO, "%s:chan_10_13 :%d\r\n", __func__, + txpwr_ofst->chan_10_13); + AICWFDBG(LOGINFO, "%s:chan_36_64 :%d\r\n", __func__, + txpwr_ofst->chan_36_64); + AICWFDBG(LOGINFO, "%s:chan_100_120:%d\r\n", __func__, + txpwr_ofst->chan_100_120); + AICWFDBG(LOGINFO, "%s:chan_122_140:%d\r\n", __func__, + txpwr_ofst->chan_122_140); + AICWFDBG(LOGINFO, "%s:chan_142_165:%d\r\n", __func__, + txpwr_ofst->chan_142_165); +} + +void get_userconfig_txpwr_ofst2x_in_fdrv(struct txpwr_ofst2x_conf *txpwr_ofst2x) +{ + int type, ch_grp; + *txpwr_ofst2x = userconfig_info.txpwr_ofst2x; + AICWFDBG(LOGINFO, "%s:enable :%d\r\n", __func__, txpwr_ofst2x->enable); + AICWFDBG(LOGDEBUG, + "pwrofst2x 2.4g: [0]:11b, [1]:ofdm_highrate, [2]:ofdm_lowrate\n chan=\t1-4\t5-9\t10-13"); + + for (type = 0; type < 3; type++) { + AICWFDBG(LOGDEBUG, "\n [%d] =", type); + for (ch_grp = 0; ch_grp < 3; ch_grp++) { + AICWFDBG(LOGDEBUG, "\t%d", + txpwr_ofst2x->pwrofst2x_tbl_2g4[type][ch_grp]); + } + } + AICWFDBG(LOGDEBUG, + "\npwrofst2x 5g: [0]:ofdm_lowrate, [1]:ofdm_highrate, [2]:ofdm_midrate\n chan=\t36-50\t51-64\t98-114\t115-130\t131-146\t147-166"); + for (type = 0; type < 3; type++) { + AICWFDBG(LOGDEBUG, "\n [%d] =", type); + for (ch_grp = 0; ch_grp < 6; ch_grp++) { + AICWFDBG(LOGDEBUG, "\t%d", + txpwr_ofst2x->pwrofst2x_tbl_5g[type][ch_grp]); + } + } + AICWFDBG(LOGDEBUG, "\n"); +} + +void get_userconfig_txpwr_idx(struct txpwr_idx_conf *txpwr_idx) +{ + memcpy(txpwr_idx, &nvram_info.txpwr_idx, sizeof(struct txpwr_idx_conf)); +} + +void get_userconfig_txpwr_ofst(struct txpwr_ofst_conf *txpwr_ofst) +{ + memcpy(txpwr_ofst, &nvram_info.txpwr_ofst, sizeof(struct txpwr_ofst_conf)); +} + +void get_userconfig_xtal_cap(struct xtal_cap_conf *xtal_cap) +{ + if (nvram_info.xtal_cap.enable) + *xtal_cap = nvram_info.xtal_cap; + + if (userconfig_info.xtal_cap.enable) + *xtal_cap = userconfig_info.xtal_cap; + + AICWFDBG(LOGINFO, "%s:enable :%d\r\n", __func__, xtal_cap->enable); + AICWFDBG(LOGINFO, "%s:xtal_cap :%d\r\n", __func__, xtal_cap->xtal_cap); + AICWFDBG(LOGINFO, "%s:xtal_cap_fine:%d\r\n", __func__, + xtal_cap->xtal_cap_fine); +} + +s8 get_txpwr_max(s8 power) +{ + int i = 0; + + for (i = 0; i <= 11; i++) { + if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[i]) + power = userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[i]; + } + for (i = 0; i <= 9; i++) { + if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[i]) + power = userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[i]; + } + for (i = 0; i <= 11; i++) { + if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[i]) + power = userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[i]; + } + for (i = 4; i <= 11; i++) { + if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[i]) + power = userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[i]; + } + for (i = 0; i <= 9; i++) { + if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[i]) + power = userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[i]; + } + for (i = 0; i <= 11; i++) { + if (power < userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[i]) + power = userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[i]; + } + + if (userconfig_info.txpwr_loss.loss_enable_2g4 == 1 || + userconfig_info.txpwr_loss.loss_enable_5g == 1) { + if (userconfig_info.txpwr_loss.loss_value_2g4 < + userconfig_info.txpwr_loss.loss_value_5g) + power += userconfig_info.txpwr_loss.loss_value_5g; + else + power += userconfig_info.txpwr_loss.loss_value_2g4; + } + + AICWFDBG(LOGDEBUG, "AICWF %s:txpwr_max:%d \r\n", __func__, power); + return power; +} + +void set_txpwr_loss_ofst(s8 value) +{ + int i = 0; + + for (i = 0; i <= 11; i++) + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[i] += value; + + for (i = 0; i <= 9; i++) + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[i] += value; + + for (i = 0; i <= 11; i++) + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[i] += value; + + for (i = 4; i <= 11; i++) + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[i] += value; + + for (i = 0; i <= 9; i++) + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[i] += value; + + for (i = 0; i <= 11; i++) + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[i] += value; + + AICWFDBG(LOGDEBUG, "AICWF %s:value:%d\r\n", __func__, value); +} + +#define MATCH_NODE(type, node, cfg_key) {cfg_key, offsetof(type, node)} + +struct parse_match_t { + char keyname[64]; + int offset; +}; + +static const char * const parse_key_prefix[] = { + [0x01] = "module0_", + [0x21] = "module1_", +}; + +static const struct parse_match_t parse_match_tab[] = { + MATCH_NODE(struct nvram_info_t, txpwr_idx.enable, "enable"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.dsss, "dsss"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdmlowrate_2g4, "ofdmlowrate_2g4"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm64qam_2g4, "ofdm64qam_2g4"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm256qam_2g4, "ofdm256qam_2g4"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm1024qam_2g4, "ofdm1024qam_2g4"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdmlowrate_5g, "ofdmlowrate_5g"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm64qam_5g, "ofdm64qam_5g"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm256qam_5g, "ofdm256qam_5g"), + MATCH_NODE(struct nvram_info_t, txpwr_idx.ofdm1024qam_5g, "ofdm1024qam_5g"), + + MATCH_NODE(struct nvram_info_t, txpwr_ofst.enable, "ofst_enable"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_1_4, "ofst_chan_1_4"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_5_9, "ofst_chan_5_9"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_10_13, "ofst_chan_10_13"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_36_64, "ofst_chan_36_64"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_100_120, "ofst_chan_100_120"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_122_140, "ofst_chan_122_140"), + MATCH_NODE(struct nvram_info_t, txpwr_ofst.chan_142_165, "ofst_chan_142_165"), + + MATCH_NODE(struct nvram_info_t, xtal_cap.enable, "xtal_enable"), + MATCH_NODE(struct nvram_info_t, xtal_cap.xtal_cap, "xtal_cap"), + MATCH_NODE(struct nvram_info_t, xtal_cap.xtal_cap_fine, "xtal_cap_fine"), +}; + +static int parse_key_val(const char *str, const char *key, char *val) +{ + const char *p = NULL; + const char *dst = NULL; + int keysize = 0; + int bufsize = 0; + + if (!str || !key || !val) + return -1; + + keysize = strlen(key); + bufsize = strlen(str); + if (bufsize <= keysize) + return -1; + + p = str; + while (*p != 0 && *p == ' ') + p++; + + if (*p == '#') + return -1; + + if (str + bufsize - p <= keysize) + return -1; + + if (strncmp(p, key, keysize) != 0) + return -1; + + p += keysize; + + while (*p != 0 && *p == ' ') + p++; + + if (*p != '=') + return -1; + + p++; + while (*p != 0 && *p == ' ') + p++; + + if (*p == '"') + p++; + + dst = p; + while (*p != 0) + p++; + + p--; + while (*p == ' ') + p--; + + if (*p == '"') + p--; + + while (*p == '\r' || *p == '\n') + p--; + + p++; + strscpy(val, dst, p - dst); + val[p - dst] = 0; + return 0; +} + +int rwnx_atoi(char *value) +{ + int len = 0; + int i = 0; + int result = 0; + int flag = 1; + + if (value[0] == '-') { + flag = -1; + value++; + } + len = strlen(value); + + for (i = 0; i < len; i++) { + result = result * 10; + if (value[i] >= 48 && value[i] <= 57) { + result += value[i] - 48; + } else { + result = 0; + break; + } + } + + return result * flag; +} + +static int rwnx_parse_mul_value(char *line, char *val[]) +{ + int nargs = 0; + + while (nargs < 10) { + while ((*line == ' ') || (*line == '\t')) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + //AICWFDBG(LOGDEBUG, "%s p1,%d\n", __func__, nargs); + return nargs; + } + + val[nargs++] = line; /* begin of argument string */ + + /* find end of string */ + while (*line && (*line != ' ') && (*line != '\t')) + ++line; + + if (*line == '\0') { /* end of line, no more args */ + //AICWFDBG(LOGDEBUG, "%s p2,%d\n", __func__, nargs); + return nargs; + } + + *line++ = '\0'; /* terminate current arg */ + } + + AICWFDBG(LOGDEBUG, "nargs: %d\n", nargs); + //AICWFDBG(LOGDEBUG, "%s exit,%d\n", __func__, nargs); + return nargs; +} + +int get_ccode_region(char *ccode) +{ + int i, cnt; + + AICWFDBG(LOGDEBUG, "%s ccode:%s\r\n", __func__, ccode); + cnt = ARRAY_SIZE(reg_tables); + + for (i = 0; i < cnt; i++) { + if (reg_tables[i].ccode[0] == ccode[0] && + reg_tables[i].ccode[1] == ccode[1]) { + AICWFDBG(LOGDEBUG, "region: %d\r\n", reg_tables[i].region); + return reg_tables[i].region; + } + } + AICWFDBG(LOGDEBUG, "use default region\r\n"); + return REGIONS_DEFAULT; +} + +void rwnx_plat_nvram_set_value(char *command, char *value) +{ + // TODO send command + AICWFDBG(LOGDEBUG, "%s:command=%s value=%s\n", __func__, command, value); + if (!strcmp(command, "enable")) { + userconfig_info.txpwr_lvl.enable = rwnx_atoi(value); + userconfig_info.txpwr_lvl_v2.enable = rwnx_atoi(value); + } else if (!strcmp(command, "dsss")) { + userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value); + } else if (!strcmp(command, "ofdmlowrate_2g4")) { + userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm64qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm256qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm1024qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdmlowrate_5g")) { + userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm64qam_5g")) { + userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm256qam_5g")) { + userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm1024qam_5g")) { + userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[10] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) { + userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[11] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_enable")) { + userconfig_info.txpwr_ofst.enable = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_1_4")) { + userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_5_9")) { + userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_10_13")) { + userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_36_64")) { + userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_100_120")) { + userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_122_140")) { + userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_142_165")) { + userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_enable")) { + userconfig_info.xtal_cap.enable = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_cap")) { + userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_cap_fine")) { + userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value); + } else { + AICWFDBG(LOGERROR, "invalid cmd: %s\n", command); + } +} + +void rwnx_plat_nvram_set_value_group(char *command, char *value) +{ + char *mul_val[10]; + int i; + + // TODO send command + AICWFDBG(LOGDEBUG, "%s:command=%s value=%s\n", __func__, command, value); + if (!strcmp(command, "enable")) { + userconfig_info.txpwr_lvl.enable = rwnx_atoi(value); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].enable = rwnx_atoi(value); + } else if (!strcmp(command, "dsss")) { + userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value); + } else if (!strcmp(command, "ofdmlowrate_2g4")) { + userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm64qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm256qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm1024qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdmlowrate_5g")) { + userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm64qam_5g")) { + userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm256qam_5g")) { + userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm1024qam_5g")) { + userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[0] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[1] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[2] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[3] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[4] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[5] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[6] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[7] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[8] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[9] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[10] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11b_11ag_2g4[11] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[0] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[1] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[2] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[3] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[4] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[5] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[6] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[7] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[8] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_2g4[9] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[0] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[1] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[2] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[3] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[4] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[5] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[6] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[7] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[8] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[9] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[10] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_2g4[11] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_1m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[0] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_2m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[1] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_5m5_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[2] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_11m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[3] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_6m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[4] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_9m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[5] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_12m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[6] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_18m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[7] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_24m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[8] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_36m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[9] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_48m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[10] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11a_54m_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11a_5g[11] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs0_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[0] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs1_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[1] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs2_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[2] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs3_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[3] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs4_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[4] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs5_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[5] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs6_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[6] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs7_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[7] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs8_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[8] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11n_11ac_mcs9_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11n_11ac_5g[9] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs0_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[0] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs1_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[1] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs2_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[2] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs3_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[3] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs4_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[4] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs5_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[5] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs6_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[6] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs7_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[7] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs8_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[8] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs9_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[9] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs10_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[10] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_11ax_mcs11_5g")) { + rwnx_parse_mul_value(value, mul_val); + for (i = 0; i < REGION_NUM_MAX; i++) + userconfig_info.txpwr_lvl_v3_gp[i].pwrlvl_11ax_5g[11] = + rwnx_atoi(mul_val[i]); + } else if (!strcmp(command, "lvl_adj_enable")) { + userconfig_info.txpwr_lvl_adj.enable = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_2g4_chan_1_4")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_2g4_chan_5_9")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_2g4_chan_10_13")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_42")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_58")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_106")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_122")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_138")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_155")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[5] = rwnx_atoi(value); + } else if (!strcmp(command, "loss_enable_2g4")) { + userconfig_info.txpwr_loss.loss_enable_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "loss_value_2g4")) { + userconfig_info.txpwr_loss.loss_value_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "loss_enable_5g")) { + userconfig_info.txpwr_loss.loss_enable_5g = rwnx_atoi(value); + } else if (!strcmp(command, "loss_value_5g")) { + userconfig_info.txpwr_loss.loss_value_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_enable")) { + userconfig_info.txpwr_ofst.enable = rwnx_atoi(value); + userconfig_info.txpwr_ofst2x.enable = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_1_4")) { + userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_5_9")) { + userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_10_13")) { + userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_36_64")) { + userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_100_120")) { + userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_122_140")) { + userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_142_165")) { + userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_11b_chan_1_4")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_11b_chan_5_9")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_11b_chan_10_13")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_1_4")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_5_9")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_10_13")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_1_4")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_5_9")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_10_13")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_42")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_58")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_106")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_122")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][3] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_138")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][4] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_155")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][5] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][3] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][4] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][5] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_42")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_58")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_106")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_122")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][3] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_138")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][4] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_155")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][5] = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_enable")) { + userconfig_info.xtal_cap.enable = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_cap")) { + userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_cap_fine")) { + userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value); + } else { + AICWFDBG(LOGERROR, "invalid cmd: %s\n", command); + } +} + +void rwnx_plat_nvram_set_value_v3(char *command, char *value) +{ + // TODO send command + AICWFDBG(LOGDEBUG, "%s:command=%s value=%s\n", __func__, command, value); + //AICWFDBG(LOGDEBUG, "AICWF command=%s value=%s\n", command, value); + if (!strcmp(command, "enable")) { + userconfig_info.txpwr_lvl.enable = rwnx_atoi(value); + userconfig_info.txpwr_lvl_v3.enable = rwnx_atoi(value); + } else if (!strcmp(command, "dsss")) { + userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value); + } else if (!strcmp(command, "ofdmlowrate_2g4")) { + userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm64qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm256qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm1024qam_2g4")) { + userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofdmlowrate_5g")) { + userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm64qam_5g")) { + userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm256qam_5g")) { + userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofdm1024qam_5g")) { + userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[10] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[11] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_1m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_2m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_5m5_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_11m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_6m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_9m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_12m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_18m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_24m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_36m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_48m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[10] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11a_54m_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[11] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs0_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs1_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs2_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs3_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs4_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs5_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs6_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs7_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs8_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11n_11ac_mcs9_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs0_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs1_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs2_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs3_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs4_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs5_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[5] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs6_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[6] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs7_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[7] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs8_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[8] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs9_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[9] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs10_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[10] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_11ax_mcs11_5g")) { + userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[11] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_enable")) { + userconfig_info.txpwr_lvl_adj.enable = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_2g4_chan_1_4")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_2g4_chan_5_9")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_2g4_chan_10_13")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_2g4[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_42")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[0] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_58")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[1] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_106")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[2] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_122")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[3] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_138")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[4] = rwnx_atoi(value); + } else if (!strcmp(command, "lvl_adj_5g_chan_155")) { + userconfig_info.txpwr_lvl_adj.pwrlvl_adj_tbl_5g[5] = rwnx_atoi(value); + } else if (!strcmp(command, "loss_enable_2g4")) { + userconfig_info.txpwr_loss.loss_enable_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "loss_value_2g4")) { + userconfig_info.txpwr_loss.loss_value_2g4 = rwnx_atoi(value); + } else if (!strcmp(command, "loss_enable_5g")) { + userconfig_info.txpwr_loss.loss_enable_5g = rwnx_atoi(value); + } else if (!strcmp(command, "loss_value_5g")) { + userconfig_info.txpwr_loss.loss_value_5g = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_enable")) { + userconfig_info.txpwr_ofst.enable = rwnx_atoi(value); + userconfig_info.txpwr_ofst2x.enable = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_1_4")) { + userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_5_9")) { + userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_10_13")) { + userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_36_64")) { + userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_100_120")) { + userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_122_140")) { + userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_chan_142_165")) { + userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_11b_chan_1_4")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_11b_chan_5_9")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_11b_chan_10_13")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[0][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_1_4")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_5_9")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_highrate_chan_10_13")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[1][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_1_4")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_5_9")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_2g4_ofdm_lowrate_chan_10_13")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_2g4[2][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_42")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_58")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_106")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_122")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][3] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_138")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][4] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_lowrate_chan_155")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[0][5] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_42")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_58")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_106")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_122")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][3] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_138")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][4] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_highrate_chan_155")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[1][5] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_42")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][0] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_58")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][1] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_106")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][2] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_122")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][3] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_138")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][4] = rwnx_atoi(value); + } else if (!strcmp(command, "ofst_5g_ofdm_midrate_chan_155")) { + userconfig_info.txpwr_ofst2x.pwrofst2x_tbl_5g[2][5] = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_enable")) { + userconfig_info.xtal_cap.enable = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_cap")) { + userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value); + } else if (!strcmp(command, "xtal_cap_fine")) { + userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value); + } else { + AICWFDBG(LOGERROR, "invalid cmd: %s\n", command); + } +} + +void rwnx_plat_userconfig_parsing2(char *buffer, int size) +{ + int i = 0; + int parse_state = 0; + char command[30]; + char value[100]; + int char_counter = 0; + + memset(command, 0, 30); + memset(value, 0, 100); + + for (i = 0; i < size; i++) { + // Send command or print nvram log when char is \r or \n + if (buffer[i] == 0x0a || buffer[i] == 0x0d) { + if (command[0] != 0 && value[0] != 0) { + if (parse_state == PRINT) + AICWFDBG(LOGINFO, "%s:%s\r\n", __func__, value); + else if (parse_state == GET_VALUE) + rwnx_plat_nvram_set_value(command, value); + } + // Reset command value and char_counter + memset(command, 0, 30); + memset(value, 0, 100); + char_counter = 0; + parse_state = INIT; + continue; + } + + // Switch parser state + if (parse_state == INIT) { + if (buffer[i] == '#') { + parse_state = PRINT; + continue; + } else if (buffer[i] == 0x0a || buffer[i] == 0x0d) { + parse_state = INIT; + continue; + } else { + parse_state = CMD; + } + } + + // Fill data to command and value + if (parse_state == PRINT) { + command[0] = 0x01; + value[char_counter] = buffer[i]; + char_counter++; + } else if (parse_state == CMD) { + if (command[0] != 0 && buffer[i] == '=') { + parse_state = GET_VALUE; + char_counter = 0; + continue; + } + command[char_counter] = buffer[i]; + char_counter++; + } else if (parse_state == GET_VALUE) { + value[char_counter] = buffer[i]; + char_counter++; + } + } +} + +void rwnx_plat_userconfig_parsing3(char *buffer, int size) +{ + int i = 0; + int parse_state = 0; + char command[64]; + char value[100]; + int char_counter = 0; + u8 area_pw = 0; + + memset(command, 0, 64); + memset(value, 0, 100); + + for (i = 0; i < size; i++) { + // Send command or print nvram log when char is \r or \n + if (buffer[i] == 0x0a || buffer[i] == 0x0d) { + if (command[0] != 0 && value[0] != 0) { + if (parse_state == PRINT) { + if (area_pw == 0 && + strnstr(value, "SRRC", strlen(value))) { + area_pw = 1; + AICWFDBG(LOGINFO, + "use diff_area_power userconfig\n"); + } + AICWFDBG(LOGINFO, "%s:%s\r\n", __func__, value); + } else if (parse_state == GET_VALUE) { + if (area_pw) + rwnx_plat_nvram_set_value_group(command, value); + else + rwnx_plat_nvram_set_value_v3(command, value); + } + } + // Reset command value and char_counter + memset(command, 0, 64); + memset(value, 0, 100); + char_counter = 0; + parse_state = INIT; + continue; + } + + // Switch parser state + if (parse_state == INIT) { + if (buffer[i] == '#') { + parse_state = PRINT; + continue; + } else if (buffer[i] == 0x0a || buffer[i] == 0x0d) { + parse_state = INIT; + continue; + } else { + parse_state = CMD; + } + } + + // Fill data to command and value + if (parse_state == PRINT) { + command[0] = 0x01; + value[char_counter] = buffer[i]; + char_counter++; + } else if (parse_state == CMD) { + if (command[0] != 0 && buffer[i] == '=') { + parse_state = GET_VALUE; + char_counter = 0; + continue; + } + command[char_counter] = buffer[i]; + char_counter++; + } else if (parse_state == GET_VALUE) { + if (buffer[i] != 0x2D && + (buffer[i] < 0x30 || buffer[i] > 0x39) && + buffer[i] != 0x20) + continue; + value[char_counter] = buffer[i]; + char_counter++; + } + } +} + +void rwnx_plat_userconfig_parsing(struct rwnx_hw *rwnx_hw, char *buffer, + int size) +{ + char conf[100], keyname[64]; + char *line; + char *data; + int i = 0, len = 0; + long val; + + if (size <= 0) { + pr_err("Config buffer size %d error\n", size); + return; + } + + AICWFDBG(LOGDEBUG, "AICWF %s rwnx_hw->vendor_info:0x%02X \r\n", __func__, + rwnx_hw->vendor_info); + if (rwnx_hw->vendor_info == 0x00 || + rwnx_hw->vendor_info > (ARRAY_SIZE(parse_key_prefix) - 1)) { + AICWFDBG(LOGDEBUG, "AICWF Unsuppor vendor info config\n"); + AICWFDBG(LOGDEBUG, "AICWF Using module0 config\n"); + rwnx_hw->vendor_info = 0x01; + // return; + } + + data = vmalloc(size + 1); + if (!data) { + AICWFDBG(LOGERROR, "%s vmalloc fail\n", __func__); + return; + } + + memcpy(data, buffer, size); + buffer = data; + + while (1) { + line = buffer; + if (*line == 0) + break; + + while (*buffer != '\r' && *buffer != '\n' && + *buffer != 0 && len++ < size) + buffer++; + + while ((*buffer == '\r' || *buffer == '\n') && len++ < size) + *buffer++ = 0; + + if (len >= size) + *buffer = 0; + + // store value to data struct + for (i = 0; i < ARRAY_SIZE(parse_match_tab); i++) { + scnprintf(keyname, sizeof(keyname), "%s%s", + parse_key_prefix[rwnx_hw->vendor_info], + parse_match_tab[i].keyname); + if (parse_key_val(line, keyname, conf) == 0) { + if (kstrtol(conf, 0, &val)) { + pr_warn("Invalid value for %s\n", keyname); + continue; + } + *(unsigned long *)((unsigned long)&nvram_info + + parse_match_tab[i].offset) = val; + AICWFDBG(LOGDEBUG, "AICWF %s, %s = %ld\n", __func__, + parse_match_tab[i].keyname, val); + break; + } + } + } + vfree(data); +} + +int rwnx_plat_userconfig_upload(struct rwnx_hw *rwnx_hw, + const char *filename) +{ + int size; + u32 *dst = NULL; + + AICWFDBG(LOGDEBUG, "AICWF userconfig file path:%s \r\n", filename); + + /* load aic firmware */ + size = rwnx_load_firmware(&dst, filename, rwnx_hw->dev); + if (size <= 0) { + AICWFDBG(LOGDEBUG, "AICWF wrong size of firmware file\n"); + vfree(dst); + dst = NULL; + return 0; + } + + /* Copy the file on the Embedded side */ + AICWFDBG(LOGDEBUG, "AICWF ### Upload %s userconfig, size=%d\n", filename, size); + + rwnx_plat_userconfig_parsing(rwnx_hw, (char *)dst, size); + + if (dst) { + vfree(dst); + dst = NULL; + } + + AICWFDBG(LOGDEBUG, "AICWF userconfig download complete\n\n"); + + return 0; +} + +#ifdef CONFIG_AIC8800_POWER_LIMIT +static inline char *get_line_from_buffer(char **buffer) +{ + return strsep(buffer, "\n"); +} + +int is_all_space_or_tab(u8 *data, u8 size) +{ + u8 cnt = 0; + u8 num_of_space_and_tab = 0; + + while (size > cnt) { + if (data[cnt] == ' ' || data[cnt] == '\t' || data[cnt] == '\0') + ++num_of_space_and_tab; + ++cnt; + } + return size == num_of_space_and_tab; +} + +int is_comment_string(char *sz_str) +{ + if (*sz_str == '#' && *(sz_str + 1) == ' ') + return 1; + else + return 0; +} + +int is_enable_limit(char *szs_str) +{ + int i = 0, value = 0; + char en[15]; + + if (*szs_str == '#' && *(szs_str + 1) == '*') { + i = 2; + while (szs_str[i] != '=' && (i < 15)) { + en[i - 2] = szs_str[i]; + i++; + } + en[i - 2] = '\0'; + i += 1; + if (strncmp(en, "enable", 6) == 0) { + value = rwnx_atoi(szs_str + i); + if (value == 1) + return 1; + else + return 0; + } + } + return -1; +} + +int parse_qualified_string(char *in, u32 *start, char *out, char left_qualifier, + char right_qualifier) +{ + u32 i = 0, j = 0; + char c = in[(*start)++]; + + if (c != left_qualifier) + return 0; + i = (*start); + c = in[(*start)++]; + while (c != right_qualifier && c != '\0') + c = in[(*start)++]; + if (c == '\0') + return 0; + j = (*start) - 2; + strscpy((char *)out, (const char *)(in + i), j - i + 1); + return 1; +} + +int get_u1_byte_integer_from_string_in_decimal(char *str, u8 *p_int) +{ + u16 i = 0; + *p_int = 0; + while (str[i] != '\0') { + if (str[i] >= '0' && str[i] <= '9') { + *p_int *= 10; + *p_int += (str[i] - '0'); + } else { + return 0; + } + ++i; + } + return 1; +} + +int get_s1_byte_integer_from_string_in_decimal(char *str, s8 *val) +{ + u8 negative = 0; + u16 i = 0; + *val = 0; + while (str[i] != '\0') { + if (i == 0 && (str[i] == '+' || str[i] == '-')) { + if (str[i] == '-') + negative = 1; + } else if (str[i] >= '0' && str[i] <= '9') { + *val *= 10; + *val += (str[i] - '0'); + } else { + return 0; + } + ++i; + } + if (negative) + *val = -*val; + return 1; +} + +static void update_power_limit(u8 band_cc, const char *channel, u8 channel_num, + const char *power_limit, u8 power_limit_val, + struct powerlimit_info_t *powerlimit_info) +{ + if (band_cc == PHY_BAND_2G4) { + u8 cur_idx = powerlimit_info->txpwr_lmt.ch_cnt_2g4; + + if (cur_idx == 0) + AICWFDBG(LOGDEBUG, "[%d]: ch=%s, pwr=%s\n", cur_idx, channel, power_limit); + if (cur_idx < MAC_DOMAINCHANNEL_24G_MAX) { + powerlimit_info->txpwr_lmt.ch_num_2g4[cur_idx] = channel_num; + powerlimit_info->txpwr_lmt.max_pwr_2g4[cur_idx] = power_limit_val; + powerlimit_info->txpwr_lmt.ch_cnt_2g4++; + } else { + AICWFDBG(LOGERROR, "band %d chan_cnt reached %d\n", + band_cc, MAC_DOMAINCHANNEL_24G_MAX); + AICWFDBG(LOGERROR, "channel=%s(%d) powerLimit=%s(%d)\n", + channel, channel_num, power_limit, power_limit_val); + } + } else if (band_cc == PHY_BAND_5G) { + u8 cur_idx = powerlimit_info->txpwr_lmt.ch_cnt_5g; + + if (cur_idx == 0) + AICWFDBG(LOGDEBUG, "[%d]: ch=%s, pwr=%s\n", cur_idx, channel, power_limit); + if (cur_idx < MAC_DOMAINCHANNEL_5G_MAX) { + powerlimit_info->txpwr_lmt.ch_num_5g[cur_idx] = channel_num; + powerlimit_info->txpwr_lmt.max_pwr_5g[cur_idx] = power_limit_val; + powerlimit_info->txpwr_lmt.ch_cnt_5g++; + } else { + AICWFDBG(LOGERROR, "band %d chan_cnt reached %d\n", + band_cc, MAC_DOMAINCHANNEL_5G_MAX); + AICWFDBG(LOGERROR, "channel=%s(%d) powerLimit=%s(%d)\n", + channel, channel_num, power_limit, power_limit_val); + } + } +} + +void rwnx_plat_powerlimit_parsing(char *buffer, int size, char *cc) +{ +#define LD_STAGE_EXC_MAPPING 0 +#define LD_STAGE_TAB_DEFINE 1 +#define LD_STAGE_TAB_START 2 +#define LD_STAGE_COLUMN_DEFINE 3 +#define LD_STAGE_CH_ROW 4 + + u8 loading_stage = LD_STAGE_EXC_MAPPING; + u32 i = 0, for_cnt = 0; + u32 i_cc; + char *sz_line, *ptmp; + char band[10], col_num_buf[10]; + u8 col_num = 0, col_num_cc = 255, band_cc = 0; + bool sp_cc = false; + // clear powerlimit info at first + memset((void *)&powerlimit_info, 0, sizeof(struct powerlimit_info_t)); + ptmp = buffer; + for (sz_line = get_line_from_buffer(&ptmp); sz_line; + sz_line = get_line_from_buffer(&ptmp)) { + if (is_all_space_or_tab(sz_line, sizeof(*sz_line))) + continue; + + if (is_enable_limit(sz_line) == 0) + return; + + if (is_comment_string(sz_line)) + continue; + + if (loading_stage == LD_STAGE_EXC_MAPPING) { + if (sz_line[0] == '#' || sz_line[1] == '#') + loading_stage = LD_STAGE_TAB_DEFINE; + else + continue; + } + + if (loading_stage == LD_STAGE_TAB_DEFINE) { + /* read "## 2.4G" */ + if (sz_line[0] != '#' || sz_line[1] != '#') + continue; + + /* skip the space */ + i = 2; + while (sz_line[i] == ' ' || sz_line[i] == '\t') + ++i; + + sz_line[--i] = + ' '; /* return the space in front of the regulation info */ + + /* Parse the label of the table */ + memset((void *)band, 0, 10); + if (!parse_qualified_string(sz_line, &i, band, ' ', ',')) { + AICWFDBG(LOGERROR, "Fail to parse band!\n"); + goto exit; + } + if (strncmp(band, "2.4G", 4) == 0) + band_cc = PHY_BAND_2G4; + else if (strncmp(band, "5G", 2) == 0) + band_cc = PHY_BAND_5G; + + memset((void *)col_num_buf, 0, 10); + if (!parse_qualified_string(sz_line, &i, col_num_buf, '#', '#')) { + AICWFDBG(LOGERROR, "Fail to parse column number!\n"); + goto exit; + } + if (!get_u1_byte_integer_from_string_in_decimal(col_num_buf, &col_num)) { + AICWFDBG(LOGERROR, + "Column number \"%s\" is not unsigned decimal\n", + col_num_buf); + goto exit; + } + if (col_num == 0) { + AICWFDBG(LOGERROR, "Column number is 0\n"); + goto exit; + } + + AICWFDBG(LOGDEBUG, "band=%s(%d)\n", band, band_cc); + loading_stage = LD_STAGE_TAB_START; + } else if (loading_stage == LD_STAGE_TAB_START) { + /* read "## START" */ + if (sz_line[0] != '#' || sz_line[1] != '#') + continue; + + /* skip the space */ + i = 2; + while (sz_line[i] == ' ' || sz_line[i] == '\t') + ++i; + + if (strncmp((u8 *)(sz_line + i), "START", 5)) { + AICWFDBG(LOGERROR, "Missing \"## START\" label\n"); + goto exit; + } + + loading_stage = LD_STAGE_COLUMN_DEFINE; + } else if (loading_stage == LD_STAGE_COLUMN_DEFINE) { + /* read "## CN US" */ + if (sz_line[0] != '#' || sz_line[1] != '#') + continue; + + /* skip the space */ + i = 2; + while (sz_line[i] == ' ' || sz_line[i] == '\t') + ++i; + + for (for_cnt = 0; for_cnt < col_num; for_cnt++) { + /* skip the space */ + while (sz_line[i] == ' ' || sz_line[i] == '\t') + i++; + i_cc = i; + + while (sz_line[i] != ' ' && sz_line[i] != '\t' && + sz_line[i] != '\0') + i++; + + if ((i - i_cc) != 2) { + AICWFDBG(LOGERROR, "CC len err\n"); + goto exit; + } else if ((sz_line[i_cc] == cc[0]) && + (sz_line[i_cc + 1] == cc[1])) { + AICWFDBG(LOGDEBUG, "CC matched: %s, col=%d\n", cc, for_cnt); + col_num_cc = for_cnt; + powerlimit_info.flags |= POWER_LIMIT_CC_MATCHED_BIT; + sp_cc = true; + break; + } + } + if (!sp_cc) { + col_num_cc = col_num - 1; + AICWFDBG(LOGDEBUG, "use 00: %s, col_num_cc=%d\n", cc, col_num_cc); + powerlimit_info.flags |= POWER_LIMIT_CC_MATCHED_BIT; + } + + loading_stage = LD_STAGE_CH_ROW; + } else if (loading_stage == LD_STAGE_CH_ROW) { + char channel[10] = {0}, power_limit[10] = {0}; + u8 channel_num, power_limit_val, cnt = 0; + + /* the table ends */ + if (sz_line[0] == '#' && sz_line[1] == '#') { + i = 2; + while (sz_line[i] == ' ' || sz_line[i] == '\t') + ++i; + + if (strncmp((u8 *)(sz_line + i), "END", 3) == 0) { + loading_stage = LD_STAGE_TAB_DEFINE; + col_num = 0; + continue; + } else { + AICWFDBG(LOGERROR, "Missing \"## END\" label\n"); + goto exit; + } + } + + if ((sz_line[0] != 'c' && sz_line[0] != 'C') || + (sz_line[1] != 'h' && sz_line[1] != 'H')) { + AICWFDBG(LOGERROR, "Wrong channel prefix: '%c','%c'(%d,%d)\n", + sz_line[0], sz_line[1], sz_line[0], sz_line[1]); + continue; + } + i = 2; /* move to the location behind 'h' */ + + /* load the channel number */ + cnt = 0; + while (sz_line[i] >= '0' && sz_line[i] <= '9') { + channel[cnt] = sz_line[i]; + ++cnt; + ++i; + } + + for (for_cnt = 0; for_cnt < col_num; ++for_cnt) { + /* skip the space between channel number and the power limit + * value + */ + while (sz_line[i] == ' ' || sz_line[i] == '\t') + ++i; + + /* load the power limit value */ + memset((void *)power_limit, 0, 10); + + if (sz_line[i] == 'N' && sz_line[i + 1] == 'A') { + /* + * means channel not available + */ + sprintf(power_limit, "%d", POWER_LIMIT_INVALID_VAL); + i += 2; + } else if ((sz_line[i] >= '0' && sz_line[i] <= '9') || + sz_line[i] == '+' || sz_line[i] == '-') { + /* case of dBm value */ + cnt = 0; + while ((sz_line[i] >= '0' && sz_line[i] <= '9') || + sz_line[i] == '+' || sz_line[i] == '-') { + power_limit[cnt] = sz_line[i]; + ++cnt; + ++i; + } + } else { + AICWFDBG + (LOGERROR, "Wrong limit expression \"%c%c\"(%d, %d)\n", + sz_line[i], sz_line[i + 1], sz_line[i], sz_line[i + 1]); + goto exit; + } + + if (for_cnt == col_num_cc) { + /* store the power limit value */ + if (get_u1_byte_integer_from_string_in_decimal + ((char *)channel, &channel_num) == 0 || + get_s1_byte_integer_from_string_in_decimal + ((char *)power_limit, &power_limit_val) == 0) { + AICWFDBG(LOGERROR, + "Illegal index of power limit table [ch %s][val %s]\n", + channel, power_limit); + goto exit; + } + + update_power_limit(band_cc, channel, channel_num, + power_limit, power_limit_val, + &powerlimit_info); + break; + } + } + } + } +exit: + return; +} + +/// 5G lower bound freq +#define PHY_FREQ_5G 5000 + +u16 phy_channel_to_freq(u8 band, int channel) +{ + if (band == PHY_BAND_2G4 && channel >= 1 && channel <= 14) { + if (channel == 14) + return 2484; + else + return 2407 + channel * 5; + } else if (band == PHY_BAND_5G && channel >= 1 && channel <= 165) { + return PHY_FREQ_5G + channel * 5; + } + return 0; +} + +s8 get_powerlimit_by_freq(u8 band, u16 freq, u8 *enable) +{ + s8 ret = POWER_LIMIT_INVALID_VAL; + u8 idx; + + if ((powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) == 0) { + *enable = 0; + } else if (powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) { + *enable = 1; + + if (band == PHY_BAND_2G4) { + u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_2g4; + + for (idx = 0; idx < idx_cnt; idx++) { + int ch_num = powerlimit_info.txpwr_lmt.ch_num_2g4[idx]; + u16 freq_tmp = phy_channel_to_freq(PHY_BAND_2G4, ch_num); + + if (freq == freq_tmp) { + ret = powerlimit_info.txpwr_lmt.max_pwr_2g4[idx]; + // AICWFDBG(LOGINFO, "[%d]: ch=%d(freq=%d), pwr=%d\n", idx, + // ch_num, freq, ret); + break; + } + } + if (idx == idx_cnt) { + AICWFDBG(LOGERROR, + "powerlimit search failed: band=%d freq=%d\n", band, + freq); + } + } else if (band == PHY_BAND_5G) { + u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_5g; + + for (idx = 0; idx < idx_cnt; idx++) { + int ch_num = powerlimit_info.txpwr_lmt.ch_num_5g[idx]; + u16 freq_tmp = phy_channel_to_freq(PHY_BAND_5G, ch_num); + + if (freq == freq_tmp) { + ret = powerlimit_info.txpwr_lmt.max_pwr_5g[idx]; + // AICWFDBG(LOGINFO, "[%d]: ch=%d(freq=%d), pwr=%d\n", idx, + // ch_num, freq, ret); + break; + } + } + if (idx == idx_cnt) { + AICWFDBG(LOGERROR, + "powerlimit search failed: band=%d freq=%d\n", band, + freq); + } + } + } + return ret; +} + +s8 get_powerlimit_by_chnum(u8 chnum, u8 *enable) +{ + s8 ret = POWER_LIMIT_INVALID_VAL; + u8 idx; + + if ((powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) == 0) { + *enable = 0; + } else if (powerlimit_info.flags & POWER_LIMIT_CC_MATCHED_BIT) { + *enable = 1; + + if (chnum <= 14) { + u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_2g4; + + for (idx = 0; idx < idx_cnt; idx++) { + u8 ch_num = powerlimit_info.txpwr_lmt.ch_num_2g4[idx]; + + if (chnum == ch_num) { + ret = powerlimit_info.txpwr_lmt.max_pwr_2g4[idx]; + // AICWFDBG(LOGINFO, "[%d]: ch=%d, pwr=%d\n", idx, ch_num, + // ret); + break; + } + } + if (idx == idx_cnt) { + AICWFDBG(LOGERROR, "powerlimit search failed: chnum=%d\n", + chnum); + } + } else if (chnum <= 165) { + u8 idx_cnt = powerlimit_info.txpwr_lmt.ch_cnt_5g; + + for (idx = 0; idx < idx_cnt; idx++) { + int ch_num = powerlimit_info.txpwr_lmt.ch_num_5g[idx]; + + if (chnum == ch_num) { + ret = powerlimit_info.txpwr_lmt.max_pwr_5g[idx]; + // AICWFDBG(LOGINFO, "[%d]: ch=%d, pwr=%d\n", idx, ch_num, + // ret); + break; + } + } + if (idx == idx_cnt) { + AICWFDBG(LOGERROR, "powerlimit search failed: chnum=%d\n", + chnum); + } + } + } + return ret; +} +#endif + +/** + * rwnx_platform_reset - Reset the platform + * + * @rwnx_plat: platform data + * + * Return: 0 on success, or %-EIO if the platform did not reset. + */ +static int rwnx_platform_reset(struct rwnx_plat *rwnx_plat) +{ + u32 regval; + +#if defined(AICWF_SDIO_SUPPORT) + return 0; +#endif + + /* + * the doc states that SOFT implies FPGA_B_RESET + * adding FPGA_B_RESET is clearer + */ + RWNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, rwnx_plat, RWNX_ADDR_SYSTEM, + SYSCTRL_MISC_CNTL_ADDR); + msleep(100); + + regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR); + + if (regval & SOFT_RESET) { + dev_err(rwnx_platform_get_dev(rwnx_plat), "reset: failed\n"); + return -EIO; + } + + RWNX_REG_WRITE(regval & ~FPGA_B_RESET, rwnx_plat, RWNX_ADDR_SYSTEM, + SYSCTRL_MISC_CNTL_ADDR); + msleep(100); + return 0; +} + +/* + * rwmx_platform_save_config() - Save hardware config before reload + * + * @rwnx_plat: Pointer to platform data + * + * Return configuration registers values. + */ +static void *rwnx_term_save_config(struct rwnx_plat *rwnx_plat) +{ + const u32 *reg_list; + u32 *reg_value, *res; + int i, size = 0; + + if (rwnx_plat->get_config_reg) + size = rwnx_plat->get_config_reg(rwnx_plat, ®_list); + + if (size <= 0) + return NULL; + + // res = kmalloc(sizeof(u32) * size, GFP_KERNEL); + res = kmalloc_array(size, sizeof(u32), GFP_KERNEL); + if (!res) + return NULL; + + reg_value = res; + for (i = 0; i < size; i++) + *reg_value++ = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, *reg_list++); + + return res; +} + +void get_userconfig_txpwr_lvl_in_fdrv(struct txpwr_lvl_conf *txpwr_lvl) +{ + txpwr_lvl->enable = userconfig_info.txpwr_lvl.enable; + txpwr_lvl->dsss = userconfig_info.txpwr_lvl.dsss; + txpwr_lvl->ofdmlowrate_2g4 = userconfig_info.txpwr_lvl.ofdmlowrate_2g4; + txpwr_lvl->ofdm64qam_2g4 = userconfig_info.txpwr_lvl.ofdm64qam_2g4; + txpwr_lvl->ofdm256qam_2g4 = userconfig_info.txpwr_lvl.ofdm256qam_2g4; + txpwr_lvl->ofdm1024qam_2g4 = userconfig_info.txpwr_lvl.ofdm1024qam_2g4; + txpwr_lvl->ofdmlowrate_5g = userconfig_info.txpwr_lvl.ofdmlowrate_5g; + txpwr_lvl->ofdm64qam_5g = userconfig_info.txpwr_lvl.ofdm64qam_5g; + txpwr_lvl->ofdm256qam_5g = userconfig_info.txpwr_lvl.ofdm256qam_5g; + txpwr_lvl->ofdm1024qam_5g = userconfig_info.txpwr_lvl.ofdm1024qam_5g; + + AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl->enable); + AICWFDBG(LOGDEBUG, "%s:dsss:%d\r\n", __func__, txpwr_lvl->dsss); + AICWFDBG(LOGDEBUG, "%s:ofdmlowrate_2g4:%d\r\n", __func__, + txpwr_lvl->ofdmlowrate_2g4); + AICWFDBG(LOGDEBUG, "%s:ofdm64qam_2g4:%d\r\n", __func__, + txpwr_lvl->ofdm64qam_2g4); + AICWFDBG(LOGDEBUG, "%s:ofdm256qam_2g4:%d\r\n", __func__, + txpwr_lvl->ofdm256qam_2g4); + AICWFDBG(LOGDEBUG, "%s:ofdm1024qam_2g4:%d\r\n", __func__, + txpwr_lvl->ofdm1024qam_2g4); + AICWFDBG(LOGDEBUG, "%s:ofdmlowrate_5g:%d\r\n", __func__, + txpwr_lvl->ofdmlowrate_5g); + AICWFDBG(LOGDEBUG, "%s:ofdm64qam_5g:%d\r\n", __func__, + txpwr_lvl->ofdm64qam_5g); + AICWFDBG(LOGDEBUG, "%s:ofdm256qam_5g:%d\r\n", __func__, + txpwr_lvl->ofdm256qam_5g); + AICWFDBG(LOGDEBUG, "%s:ofdm1024qam_5g:%d\r\n", __func__, + txpwr_lvl->ofdm1024qam_5g); +} + +void get_userconfig_txpwr_lvl_v2_in_fdrv(struct txpwr_lvl_conf_v2 *txpwr_lvl_v2) +{ + *txpwr_lvl_v2 = userconfig_info.txpwr_lvl_v2; + + AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]); + AICWFDBG(LOGDEBUG, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]); + AICWFDBG(LOGDEBUG, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[0]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[1]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[2]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[3]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[4]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[5]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[6]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[7]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[8]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[9]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[10]); + AICWFDBG(LOGDEBUG, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__, + txpwr_lvl_v2->pwrlvl_11ax_2g4[11]); +} + +void get_userconfig_txpwr_lvl_group_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3, int index_reg) +{ + *txpwr_lvl_v3 = userconfig_info.txpwr_lvl_v3_gp[index_reg]; + + AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_v3->enable); + AICWFDBG(LOGDEBUG, "lvl_11b_11ag_1m_2g4:%d\r\n", txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]); +} + +void get_userconfig_txpwr_lvl_v3_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3) +{ + *txpwr_lvl_v3 = userconfig_info.txpwr_lvl_v3; + + AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_v3->enable); + AICWFDBG(LOGDEBUG, "lvl_11b_11ag_1m_2g4:%d\r\n", txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]); +} + +void get_userconfig_txpwr_lvl_adj_in_fdrv(struct txpwr_lvl_adj_conf *txpwr_lvl_adj) +{ + *txpwr_lvl_adj = userconfig_info.txpwr_lvl_adj; + + AICWFDBG(LOGDEBUG, "%s:enable:%d\r\n", __func__, txpwr_lvl_adj->enable); + AICWFDBG(LOGDEBUG, "lvl_adj_2g4_chan_1_4:%d\r\n", txpwr_lvl_adj->pwrlvl_adj_tbl_2g4[0]); +} + +/* + * rwnx_plat_userconfig_load ---Load aic_userconfig.txt + *@filename name of config + */ +static int rwnx_plat_userconfig_load(struct rwnx_hw *rwnx_hw) +{ + aic_chip_userconfig_load(rwnx_hw); +#ifdef CONFIG_AIC8800_POWER_LIMIT + aic_chip_powerlimit_load(rwnx_hw); +#endif + return 0; +} + +void get_userconfig_txpwr_loss(struct txpwr_loss_conf *txpwr_loss) +{ + txpwr_loss->loss_enable_2g4 = userconfig_info.txpwr_loss.loss_enable_2g4; + txpwr_loss->loss_value_2g4 = userconfig_info.txpwr_loss.loss_value_2g4; + txpwr_loss->loss_enable_5g = userconfig_info.txpwr_loss.loss_enable_5g; + txpwr_loss->loss_value_5g = userconfig_info.txpwr_loss.loss_value_5g; + + AICWFDBG(LOGDEBUG, + "%s:loss_enable_2g4: %d, val_2g4: %d, loss_enable_5g: %d, val_5g: %d\r\n", + __func__, + txpwr_loss->loss_enable_2g4, txpwr_loss->loss_value_2g4, + txpwr_loss->loss_enable_5g, txpwr_loss->loss_value_5g); +} + +/** + * rwnx_platform_on - Start the platform + * + * @rwnx_hw: Main driver data + * @config: Config to restore (NULL if nothing to restore) + * + * It starts the platform : + * - load fw and ucodes + * - initialize IPC + * - boot the fw + * - enable link communication/IRQ + * + * Called by 802.11 part + * + * Return: 0 on success, or a negative error code. + */ +int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config) +{ + int ret; + struct rwnx_plat *rwnx_plat = rwnx_hw->plat; + (void)ret; + + RWNX_DBG(RWNX_FN_ENTRY_STR); + + if (rwnx_plat->enabled) + return 0; + + rwnx_plat_userconfig_load(rwnx_hw); + // rwnx_plat->enabled = true; + return 0; +} + +/** + * rwnx_platform_off() - Stop the platform + * + * @rwnx_hw: Main driver data + * @config: Updated with pointer to config, to be able to restore it with + * rwnx_platform_on(). It's up to the caller to free the config. Set to NULL + * if configuration is not needed. + * + * Called by 802.11 part + */ +void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config) +{ +#if defined(AICWF_SDIO_SUPPORT) + tasklet_kill(&rwnx_hw->task); + rwnx_hw->plat->enabled = false; + return; +#endif + + if (!rwnx_hw->plat->enabled) { + if (config) + *config = NULL; + return; + } + + if (config) + *config = rwnx_term_save_config(rwnx_hw->plat); + + rwnx_hw->plat->disable(rwnx_hw); + + tasklet_kill(&rwnx_hw->task); + rwnx_platform_reset(rwnx_hw->plat); + + rwnx_hw->plat->enabled = false; +} + +/** + * rwnx_platform_init() - Initialize the platform + * + * @rwnx_plat: platform data (already updated by platform driver) + * @platform_data: Pointer to store the main driver data pointer (aka rwnx_hw) + * That will be set as driver data for the platform driver + * Return: 0 on success, < 0 otherwise + * + * Called by the platform driver after it has been probed + */ +int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); + + rwnx_plat->enabled = false; + g_rwnx_plat = rwnx_plat; + + return rwnx_cfg80211_init(rwnx_plat, platform_data); +} + +/** + * rwnx_platform_deinit() - Deinitialize the platform + * + * @rwnx_hw: main driver data + * + * Called by the platform driver after it is removed + */ +void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); + + rwnx_cfg80211_deinit(rwnx_hw); +} + +struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat) +{ +#ifdef AICWF_SDIO_SUPPORT + return rwnx_plat->sdiodev->dev; +#endif +} + +MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_AGC_FW_NAME); +MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_FCU_FW_NAME); +MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_LDPC_RAM_NAME); + +MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_MAC_FW_NAME); +MODULE_FIRMWARE(AIC8800_FW_DIR RWNX_MAC_FW_NAME2); diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h new file mode 100644 index 0000000000000..efcff3b08b2a3 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_platform.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RWNX_PLATFORM_H_ +#define _RWNX_PLATFORM_H_ + +#include "lmac_msg.h" +#include + +#define AIC8800_FW_DIR "aic8800/" + +#define RWNX_CONFIG_FW_NAME "rwnx_settings.ini" +#define RWNX_PHY_CONFIG_TRD_NAME "rwnx_trident.ini" +#define RWNX_PHY_CONFIG_KARST_NAME "rwnx_karst.ini" +#define RWNX_AGC_FW_NAME "agcram.bin" +#define RWNX_LDPC_RAM_NAME "ldpcram.bin" + +#define RWNX_MAC_FW_BASE_NAME "fmacfw" + +#define RWNX_MAC_FW_NAME RWNX_MAC_FW_BASE_NAME ".ihex" +#define RWNX_MAC_FW_NAME2 RWNX_MAC_FW_BASE_NAME ".bin" + +#define RWNX_FCU_FW_NAME "fcuram.bin" +#if (defined(CONFIG_DPD) && !defined(CONFIG_FORCE_DPD_CALIB)) +#define FW_DPDRESULT_NAME_8800DC "aic_dpdresult_lite_8800dc.bin" +#endif + +#define POWER_LEVEL_INVALID_VAL (127) + +/* + * Type of memory to access (cf rwnx_plat.get_address) + * + * @RWNX_ADDR_CPU To access memory of the embedded CPU + * @RWNX_ADDR_SYSTEM To access memory/registers of one subsystem of the + * embedded system + * + */ +enum rwnx_platform_addr { + RWNX_ADDR_CPU, + RWNX_ADDR_SYSTEM, + RWNX_ADDR_MAX, +}; + +enum regions_code { + REGIONS_SRRC, + REGIONS_FCC, + REGIONS_ETSI, + REGIONS_JP, + REGIONS_DEFAULT, +}; + +struct rwnx_hw; +struct device; +struct firmware; + +int aicwf_request_firmware(const struct firmware **fw, const char *name, + struct device *dev); + +/** + * struct rwnx_plat - Operations and state for an RWNX platform + * + * @pci_dev: pointer to pci dev + * @sdiodev: SDIO device data + * @enabled: Set if embedded platform has been enabled (i.e. fw loaded and + * ipc started) + * @enable: Configure communication with the fw (i.e. configure the transfers + * enable and register interrupt) + * @disable: Stop communication with the fw + * @deinit: Free all resources allocated for the embedded platform + * @get_address: Return the virtual address to access the requested address on + * the platform. + * @ack_irq: Acknowledge the irq at link level. + * @get_config_reg: Return the list (size + pointer) of registers to restore in + * order to reload the platform while keeping the current configuration. + * + * @priv: Private data for the link driver + */ +struct rwnx_plat { + struct pci_dev *pci_dev; + +#ifdef AICWF_SDIO_SUPPORT + struct aic_sdio_dev *sdiodev; +#endif + + bool enabled; + + int (*enable)(struct rwnx_hw *rwnx_hw); + int (*disable)(struct rwnx_hw *rwnx_hw); + void (*deinit)(struct rwnx_plat *rwnx_plat); + u8 __iomem *(*get_address)(struct rwnx_plat *rwnx_plat, int addr_name, + unsigned int offset); + void (*ack_irq)(struct rwnx_plat *rwnx_plat); + int (*get_config_reg)(struct rwnx_plat *rwnx_plat, const u32 **list); + + u8 priv[0] __aligned(sizeof(void *)); +}; + +static inline void __iomem *RWNX_ADDR(void *plat, int base, int offset) +{ + struct rwnx_plat *p = (struct rwnx_plat *)plat; + + return p->get_address(p, base, offset); +} + +static inline u32 RWNX_REG_READ(void *plat, int base, int offset) +{ + struct rwnx_plat *p = (struct rwnx_plat *)plat; + + return readl(p->get_address(p, base, offset)); +} + +static inline void RWNX_REG_WRITE(u32 val, void *plat, int base, int offset) +{ + struct rwnx_plat *p = (struct rwnx_plat *)plat; + + writel(val, p->get_address(p, base, offset)); +} + +extern struct rwnx_plat *g_rwnx_plat; + +int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data); +void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw); + +int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config); +void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config); + +int rwnx_platform_register_drv(void); +void rwnx_platform_unregister_drv(void); + +void get_userconfig_txpwr_idx(struct txpwr_idx_conf *txpwr_idx); +void get_userconfig_txpwr_ofst(struct txpwr_ofst_conf *txpwr_ofst); +void get_userconfig_xtal_cap(struct xtal_cap_conf *xtal_cap); +s8 get_txpwr_max(s8 power); +void set_txpwr_loss_ofst(s8 value); + +int get_ccode_region(char *ccode); + +void rwnx_release_firmware_common(u32 **buffer); + +int rwnx_plat_userconfig_upload(struct rwnx_hw *rwnx_hw, + const char *filename); +void rwnx_plat_userconfig_parsing(struct rwnx_hw *rwnx_hw, char *buffer, int size); +void rwnx_plat_userconfig_parsing2(char *buffer, int size); +void rwnx_plat_userconfig_parsing3(char *buffer, int size); + +void get_userconfig_txpwr_lvl_in_fdrv(struct txpwr_lvl_conf *txpwr_lvl); +void get_userconfig_txpwr_lvl_v2_in_fdrv(struct txpwr_lvl_conf_v2 *txpwr_lvl_v2); +void get_userconfig_txpwr_lvl_group_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3, int index_reg); +void get_userconfig_txpwr_lvl_v3_in_fdrv(struct txpwr_lvl_conf_v3 *txpwr_lvl_v3); +void get_userconfig_txpwr_lvl_adj_in_fdrv(struct txpwr_lvl_adj_conf *txpwr_lvl_adj); + +void get_userconfig_txpwr_ofst_in_fdrv(struct txpwr_ofst_conf *txpwr_ofst); +void get_userconfig_txpwr_ofst2x_in_fdrv(struct txpwr_ofst2x_conf *txpwr_ofst2x); +void get_userconfig_txpwr_loss(struct txpwr_loss_conf *txpwr_loss); +#ifdef CONFIG_AIC8800_POWER_LIMIT +int is_all_space_or_tab(u8 *data, u8 size); +int is_comment_string(char *sz_str); +int is_enable_limit(char *sz_str); +int parse_qualified_string(char *in, u32 *start, char *out, char left_qualifier, + char right_qualifier); +int get_u1_byte_integer_from_string_in_decimal(char *str, u8 *p_int); +int get_s1_byte_integer_from_string_in_decimal(char *str, s8 *val); +void rwnx_plat_powerlimit_parsing(char *buffer, int size, char *cc); +s8 get_powerlimit_by_freq(u8 band, u16 freq, u8 *enable); +s8 get_powerlimit_by_chnum(u8 chnum, u8 *enable); +u16 phy_channel_to_freq(u8 band, int channel); +#endif + +struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat); + +int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw, u32 **buffer, + const char *filename); +int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr, + char *filename); +int rwnx_atoi(char *value); +void rwnx_plat_nvram_set_value(char *command, char *value); +void rwnx_plat_nvram_set_value_group(char *command, char *value); +void rwnx_plat_nvram_set_value_v3(char *command, char *value); + +static inline unsigned int rwnx_platform_get_irq(struct rwnx_plat *rwnx_plat) +{ + return rwnx_plat->pci_dev->irq; +} + +#endif /* _RWNX_PLATFORM_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h new file mode 100644 index 0000000000000..093e243f3c94f --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_prof.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RWNX_PROF_H_ +#define _RWNX_PROF_H_ + +#include "reg_access.h" +#include "rwnx_platform.h" +#include "rwnx_radar.h" + +static inline void rwnx_prof_set(struct rwnx_hw *rwnx_hw, int val) +{ + struct rwnx_plat *rwnx_plat = rwnx_hw->plat; + + RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM, + NXMAC_SW_SET_PROFILING_ADDR); +} + +static inline void rwnx_prof_clear(struct rwnx_hw *rwnx_hw, int val) +{ + struct rwnx_plat *rwnx_plat = rwnx_hw->plat; + + RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM, + NXMAC_SW_CLEAR_PROFILING_ADDR); +} + +enum { + SW_PROF_HOSTBUF_IDX = 12, + /****** IPC IRQs related signals ******/ + /* E2A direction */ + SW_PROF_IRQ_E2A_RXDESC = + 16, // to make sure we let 16 bits available for LMAC FW + SW_PROF_IRQ_E2A_TXCFM, + SW_PROF_IRQ_E2A_DBG, + SW_PROF_IRQ_E2A_MSG, + SW_PROF_IPC_MSGPUSH, + SW_PROF_MSGALLOC, + SW_PROF_MSGIND, + SW_PROF_DBGIND, + + /* A2E direction */ + SW_PROF_IRQ_A2E_TXCFM_BACK, + + /****** Driver functions related signals ******/ + SW_PROF_WAIT_QUEUE_STOP, + SW_PROF_WAIT_QUEUE_WAKEUP, + SW_PROF_RWNXDATAIND, + SW_PROF_RWNX_IPC_IRQ_HDLR, + SW_PROF_RWNX_IPC_THR_IRQ_HDLR, + SW_PROF_IEEE80211RX, + SW_PROF_RWNX_PATTERN, + SW_PROF_MAX +}; + +// [LT]For debug purpose only +#if (0) +#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT (21) +#define SW_PROF_CHAN_CTXT_CFM_BIT (22) +#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (23) +#define SW_PROF_CHAN_CTXT_PUSH_BIT (24) +#define SW_PROF_CHAN_CTXT_QUEUE_BIT (25) +#define SW_PROF_CHAN_CTXT_TX_BIT (26) +#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT (27) +#define SW_PROF_CHAN_CTXT_PSWTCH_BIT (28) +#define SW_PROF_CHAN_CTXT_SWTCH_BIT (29) + +#else +#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT (0) +#define SW_PROF_CHAN_CTXT_CFM_BIT (0) +#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (0) +#define SW_PROF_CHAN_CTXT_PUSH_BIT (0) +#define SW_PROF_CHAN_CTXT_QUEUE_BIT (0) +#define SW_PROF_CHAN_CTXT_TX_BIT (0) +#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT (0) +#define SW_PROF_CHAN_CTXT_PSWTCH_BIT (0) +#define SW_PROF_CHAN_CTXT_SWTCH_BIT (0) +#endif + +static inline void REG_SW_SET_PROFILING_CHAN(void *env, int bit) +{ +#if (0) + rwnx_prof_clear((struct rwnx_hw *)env, BIT(bit)); +#else + +#endif +} + +static inline void REG_SW_CLEAR_PROFILING_CHAN(void *env, int bit) +{ +#if (0) + rwnx_prof_clear((struct rwnx_hw *)env, BIT(bit)); +#else + +#endif +} + +#ifdef CONFIG_RWNX_SW_PROFILING +/* Macros for SW PRofiling registers access */ +static inline void REG_SW_SET_PROFILING(void *env, int bit) +{ + rwnx_prof_set((struct rwnx_hw *)env, BIT(bit)); +} + +static inline void REG_SW_SET_HOSTBUF_IDX_PROFILING(void *env, int val) +{ + rwnx_prof_set((struct rwnx_hw *)env, (val) << (SW_PROF_HOSTBUF_IDX)); +} + +static inline void REG_SW_CLEAR_PROFILING(void *env, int bit) +{ + rwnx_prof_clear((struct rwnx_hw *)env, BIT(bit)); +} + +static inline void REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(void *env) +{ + rwnx_prof_clear((struct rwnx_hw *)env, 0x0F << (SW_PROF_HOSTBUF_IDX)); +} + +#else +static inline void REG_SW_SET_PROFILING(void *env, int value) +{ + (void)env; + (void)value; +} + +static inline void REG_SW_CLEAR_PROFILING(void *env, int value) +{ + (void)env; + (void)value; +} + +static inline void REG_SW_SET_HOSTBUF_IDX_PROFILING(void *env, int val) +{ + (void)env; + (void)val; +} + +static inline void REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(void *env) +{ + (void)env; +} +#endif + +#endif /* _RWNX_PROF_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c new file mode 100644 index 0000000000000..50e81d81abf1e --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_strs.c + * + * @brief Miscellaneous debug strings + * + **************************************************************************************** + */ + +#include "lmac_msg.h" +#include "rwnx_strs.h" +static const char *const rwnx_mmid2str[MSG_I(MM_MAX)] = { + [MSG_I(MM_RESET_REQ)] = "MM_RESET_REQ", + [MSG_I(MM_RESET_CFM)] = "MM_RESET_CFM", + [MSG_I(MM_START_REQ)] = "MM_START_REQ", + [MSG_I(MM_START_CFM)] = "MM_START_CFM", + [MSG_I(MM_VERSION_REQ)] = "MM_VERSION_REQ", + [MSG_I(MM_VERSION_CFM)] = "MM_VERSION_CFM", + [MSG_I(MM_ADD_IF_REQ)] = "MM_ADD_IF_REQ", + [MSG_I(MM_ADD_IF_CFM)] = "MM_ADD_IF_CFM", + [MSG_I(MM_REMOVE_IF_REQ)] = "MM_REMOVE_IF_REQ", + [MSG_I(MM_REMOVE_IF_CFM)] = "MM_REMOVE_IF_CFM", + [MSG_I(MM_STA_ADD_REQ)] = "MM_STA_ADD_REQ", + [MSG_I(MM_STA_ADD_CFM)] = "MM_STA_ADD_CFM", + [MSG_I(MM_STA_DEL_REQ)] = "MM_STA_DEL_REQ", + [MSG_I(MM_STA_DEL_CFM)] = "MM_STA_DEL_CFM", + [MSG_I(MM_SET_FILTER_REQ)] = "MM_SET_FILTER_REQ", + [MSG_I(MM_SET_FILTER_CFM)] = "MM_SET_FILTER_CFM", + [MSG_I(MM_SET_CHANNEL_REQ)] = "MM_SET_CHANNEL_REQ", + [MSG_I(MM_SET_CHANNEL_CFM)] = "MM_SET_CHANNEL_CFM", + [MSG_I(MM_SET_DTIM_REQ)] = "MM_SET_DTIM_REQ", + [MSG_I(MM_SET_DTIM_CFM)] = "MM_SET_DTIM_CFM", + [MSG_I(MM_SET_BEACON_INT_REQ)] = "MM_SET_BEACON_INT_REQ", + [MSG_I(MM_SET_BEACON_INT_CFM)] = "MM_SET_BEACON_INT_CFM", + [MSG_I(MM_SET_BASIC_RATES_REQ)] = "MM_SET_BASIC_RATES_REQ", + [MSG_I(MM_SET_BASIC_RATES_CFM)] = "MM_SET_BASIC_RATES_CFM", + [MSG_I(MM_SET_BSSID_REQ)] = "MM_SET_BSSID_REQ", + [MSG_I(MM_SET_BSSID_CFM)] = "MM_SET_BSSID_CFM", + [MSG_I(MM_SET_EDCA_REQ)] = "MM_SET_EDCA_REQ", + [MSG_I(MM_SET_EDCA_CFM)] = "MM_SET_EDCA_CFM", + [MSG_I(MM_SET_MODE_REQ)] = "MM_SET_MODE_REQ", + [MSG_I(MM_SET_MODE_CFM)] = "MM_SET_MODE_CFM", + [MSG_I(MM_SET_VIF_STATE_REQ)] = "MM_SET_VIF_STATE_REQ", + [MSG_I(MM_SET_VIF_STATE_CFM)] = "MM_SET_VIF_STATE_CFM", + [MSG_I(MM_SET_SLOTTIME_REQ)] = "MM_SET_SLOTTIME_REQ", + [MSG_I(MM_SET_SLOTTIME_CFM)] = "MM_SET_SLOTTIME_CFM", + [MSG_I(MM_SET_IDLE_REQ)] = "MM_SET_IDLE_REQ", + [MSG_I(MM_SET_IDLE_CFM)] = "MM_SET_IDLE_CFM", + [MSG_I(MM_KEY_ADD_REQ)] = "MM_KEY_ADD_REQ", + [MSG_I(MM_KEY_ADD_CFM)] = "MM_KEY_ADD_CFM", + [MSG_I(MM_KEY_DEL_REQ)] = "MM_KEY_DEL_REQ", + [MSG_I(MM_KEY_DEL_CFM)] = "MM_KEY_DEL_CFM", + [MSG_I(MM_BA_ADD_REQ)] = "MM_BA_ADD_REQ", + [MSG_I(MM_BA_ADD_CFM)] = "MM_BA_ADD_CFM", + [MSG_I(MM_BA_DEL_REQ)] = "MM_BA_DEL_REQ", + [MSG_I(MM_BA_DEL_CFM)] = "MM_BA_DEL_CFM", + [MSG_I(MM_PRIMARY_TBTT_IND)] = "MM_PRIMARY_TBTT_IND", + [MSG_I(MM_SECONDARY_TBTT_IND)] = "MM_SECONDARY_TBTT_IND", + [MSG_I(MM_SET_POWER_REQ)] = "MM_SET_POWER_REQ", + [MSG_I(MM_SET_POWER_CFM)] = "MM_SET_POWER_CFM", + [MSG_I(MM_DBG_TRIGGER_REQ)] = "MM_DBG_TRIGGER_REQ", + [MSG_I(MM_SET_PS_MODE_REQ)] = "MM_SET_PS_MODE_REQ", + [MSG_I(MM_SET_PS_MODE_CFM)] = "MM_SET_PS_MODE_CFM", + [MSG_I(MM_CHAN_CTXT_ADD_REQ)] = "MM_CHAN_CTXT_ADD_REQ", + [MSG_I(MM_CHAN_CTXT_ADD_CFM)] = "MM_CHAN_CTXT_ADD_CFM", + [MSG_I(MM_CHAN_CTXT_DEL_REQ)] = "MM_CHAN_CTXT_DEL_REQ", + [MSG_I(MM_CHAN_CTXT_DEL_CFM)] = "MM_CHAN_CTXT_DEL_CFM", + [MSG_I(MM_CHAN_CTXT_LINK_REQ)] = "MM_CHAN_CTXT_LINK_REQ", + [MSG_I(MM_CHAN_CTXT_LINK_CFM)] = "MM_CHAN_CTXT_LINK_CFM", + [MSG_I(MM_CHAN_CTXT_UNLINK_REQ)] = "MM_CHAN_CTXT_UNLINK_REQ", + [MSG_I(MM_CHAN_CTXT_UNLINK_CFM)] = "MM_CHAN_CTXT_UNLINK_CFM", + [MSG_I(MM_CHAN_CTXT_UPDATE_REQ)] = "MM_CHAN_CTXT_UPDATE_REQ", + [MSG_I(MM_CHAN_CTXT_UPDATE_CFM)] = "MM_CHAN_CTXT_UPDATE_CFM", + [MSG_I(MM_CHAN_CTXT_SCHED_REQ)] = "MM_CHAN_CTXT_SCHED_REQ", + [MSG_I(MM_CHAN_CTXT_SCHED_CFM)] = "MM_CHAN_CTXT_SCHED_CFM", + [MSG_I(MM_BCN_CHANGE_REQ)] = "MM_BCN_CHANGE_REQ", + [MSG_I(MM_BCN_CHANGE_CFM)] = "MM_BCN_CHANGE_CFM", + [MSG_I(MM_TIM_UPDATE_REQ)] = "MM_TIM_UPDATE_REQ", + [MSG_I(MM_TIM_UPDATE_CFM)] = "MM_TIM_UPDATE_CFM", + [MSG_I(MM_CONNECTION_LOSS_IND)] = "MM_CONNECTION_LOSS_IND", + [MSG_I(MM_CHANNEL_SWITCH_IND)] = "MM_CHANNEL_SWITCH_IND", + [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = "MM_CHANNEL_PRE_SWITCH_IND", + [MSG_I(MM_REMAIN_ON_CHANNEL_REQ)] = "MM_REMAIN_ON_CHANNEL_REQ", + [MSG_I(MM_REMAIN_ON_CHANNEL_CFM)] = "MM_REMAIN_ON_CHANNEL_CFM", + [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND", + [MSG_I(MM_PS_CHANGE_IND)] = "MM_PS_CHANGE_IND", + [MSG_I(MM_TRAFFIC_REQ_IND)] = "MM_TRAFFIC_REQ_IND", + [MSG_I(MM_SET_PS_OPTIONS_REQ)] = "MM_SET_PS_OPTIONS_REQ", + [MSG_I(MM_SET_PS_OPTIONS_CFM)] = "MM_SET_PS_OPTIONS_CFM", + [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = "MM_P2P_VIF_PS_CHANGE_IND", + [MSG_I(MM_CSA_COUNTER_IND)] = "MM_CSA_COUNTER_IND", + [MSG_I(MM_CHANNEL_SURVEY_IND)] = "MM_CHANNEL_SURVEY_IND", + [MSG_I(MM_SET_P2P_NOA_REQ)] = "MM_SET_P2P_NOA_REQ", + [MSG_I(MM_SET_P2P_OPPPS_REQ)] = "MM_SET_P2P_OPPPS_REQ", + [MSG_I(MM_SET_P2P_NOA_CFM)] = "MM_SET_P2P_NOA_CFM", + [MSG_I(MM_SET_P2P_OPPPS_CFM)] = "MM_SET_P2P_OPPPS_CFM", + [MSG_I(MM_CFG_RSSI_REQ)] = "MM_CFG_RSSI_REQ", + [MSG_I(MM_RSSI_STATUS_IND)] = "MM_RSSI_STATUS_IND", + [MSG_I(MM_CSA_FINISH_IND)] = "MM_CSA_FINISH_IND", + [MSG_I(MM_CSA_TRAFFIC_IND)] = "MM_CSA_TRAFFIC_IND", + [MSG_I(MM_MU_GROUP_UPDATE_REQ)] = "MM_MU_GROUP_UPDATE_REQ", + [MSG_I(MM_MU_GROUP_UPDATE_CFM)] = "MM_MU_GROUP_UPDATE_CFM", + + [MSG_I(MM_SET_ARPOFFLOAD_REQ)] = "MM_SET_ARPOFFLOAD_REQ", + [MSG_I(MM_SET_ARPOFFLOAD_CFM)] = "MM_SET_ARPOFFLOAD_CFM", + [MSG_I(MM_SET_AGG_DISABLE_REQ)] = "MM_SET_AGG_DISABLE_REQ", + [MSG_I(MM_SET_AGG_DISABLE_CFM)] = "MM_SET_AGG_DISABLE_CFM", + [MSG_I(MM_SET_COEX_REQ)] = "MM_SET_COEX_REQ", + [MSG_I(MM_SET_COEX_CFM)] = "MM_SET_COEX_CFM", + [MSG_I(MM_SET_RF_CONFIG_REQ)] = "MM_SET_RF_CONFIG_REQ", + [MSG_I(MM_SET_RF_CONFIG_CFM)] = "MM_SET_RF_CONFIG_CFM", + [MSG_I(MM_SET_RF_CALIB_REQ)] = "MM_SET_RF_CALIB_REQ", + [MSG_I(MM_SET_RF_CALIB_CFM)] = "MM_SET_RF_CALIB_CFM", + + [MSG_I(MM_GET_MAC_ADDR_REQ)] = "MM_GET_MAC_ADDR_REQ", + [MSG_I(MM_GET_MAC_ADDR_CFM)] = "MM_GET_MAC_ADDR_CFM", + [MSG_I(MM_GET_STA_INFO_REQ)] = "MM_GET_STA_INFO_REQ", + [MSG_I(MM_GET_STA_INFO_CFM)] = "MM_GET_STA_INFO_CFM", + [MSG_I(MM_SET_TXPWR_IDX_LVL_REQ)] = "MM_SET_TXPWR_IDX_LVL_REQ", + [MSG_I(MM_SET_TXPWR_IDX_LVL_CFM)] = "MM_SET_TXPWR_IDX_LVL_CFM", + [MSG_I(MM_SET_TXPWR_OFST_REQ)] = "MM_SET_TXPWR_OFST_REQ", + [MSG_I(MM_SET_TXPWR_OFST_CFM)] = "MM_SET_TXPWR_OFST_CFM", + [MSG_I(MM_SET_STACK_START_REQ)] = "MM_SET_STACK_START_REQ", + [MSG_I(MM_SET_STACK_START_CFM)] = "MM_SET_STACK_START_CFM", + [MSG_I(MM_APM_STALOSS_IND)] = "MM_APM_STALOSS_IND", + [MSG_I(MM_SET_VENDOR_HWCONFIG_REQ)] = "MM_SET_VENDOR_HWCONFIG_REQ", + [MSG_I(MM_SET_VENDOR_HWCONFIG_CFM)] = "MM_SET_VENDOR_HWCONFIG_CFM", + [MSG_I(MM_GET_FW_VERSION_REQ)] = "MM_GET_FW_VERSION_REQ", + [MSG_I(MM_GET_FW_VERSION_CFM)] = "MM_GET_FW_VERSION_CFM", + [MSG_I(MM_SET_RESUME_RESTORE_REQ)] = "MM_SET_RESUME_RESTORE_REQ", + [MSG_I(MM_SET_RESUME_RESTORE_CFM)] = "MM_SET_RESUME_RESTORE_CFM", + [MSG_I(MM_GET_WIFI_DISABLE_REQ)] = "MM_GET_WIFI_DISABLE_REQ", + [MSG_I(MM_GET_WIFI_DISABLE_CFM)] = "MM_GET_WIFI_DISABLE_CFM", + [MSG_I(MM_CFG_RSSI_CFM)] = "MM_CFG_RSSI_CFM", + [MSG_I(MM_SET_VENDOR_SWCONFIG_REQ)] = "MM_SET_VENDOR_SWCONFIG_REQ", + [MSG_I(MM_SET_VENDOR_SWCONFIG_CFM)] = "MM_SET_VENDOR_SWCONFIG_CFM", +}; + +static const char *const rwnx_dbgid2str[MSG_I(DBG_MAX)] = { + [MSG_I(DBG_MEM_READ_REQ)] = "DBG_MEM_READ_REQ", + [MSG_I(DBG_MEM_READ_CFM)] = "DBG_MEM_READ_CFM", + [MSG_I(DBG_MEM_WRITE_REQ)] = "DBG_MEM_WRITE_REQ", + [MSG_I(DBG_MEM_WRITE_CFM)] = "DBG_MEM_WRITE_CFM", + [MSG_I(DBG_SET_MOD_FILTER_REQ)] = "DBG_SET_MOD_FILTER_REQ", + [MSG_I(DBG_SET_MOD_FILTER_CFM)] = "DBG_SET_MOD_FILTER_CFM", + [MSG_I(DBG_SET_SEV_FILTER_REQ)] = "DBG_SET_SEV_FILTER_REQ", + [MSG_I(DBG_SET_SEV_FILTER_CFM)] = "DBG_SET_SEV_FILTER_CFM", + [MSG_I(DBG_ERROR_IND)] = "DBG_ERROR_IND", + [MSG_I(DBG_GET_SYS_STAT_REQ)] = "DBG_GET_SYS_STAT_REQ", + [MSG_I(DBG_GET_SYS_STAT_CFM)] = "DBG_GET_SYS_STAT_CFM", +}; + +static const char *const rwnx_scanid2str[MSG_I(SCAN_MAX)] = { + [MSG_I(SCAN_START_REQ)] = "SCAN_START_REQ", + [MSG_I(SCAN_START_CFM)] = "SCAN_START_CFM", + [MSG_I(SCAN_DONE_IND)] = "SCAN_DONE_IND", +}; + +static const char *const rwnx_tdlsid2str[MSG_I(TDLS_MAX)] = { + [MSG_I(TDLS_CHAN_SWITCH_CFM)] = "TDLS_CHAN_SWITCH_CFM", + [MSG_I(TDLS_CHAN_SWITCH_REQ)] = "TDLS_CHAN_SWITCH_REQ", + [MSG_I(TDLS_CHAN_SWITCH_IND)] = "TDLS_CHAN_SWITCH_IND", + [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = "TDLS_CHAN_SWITCH_BASE_IND", + [MSG_I(TDLS_CANCEL_CHAN_SWITCH_REQ)] = "TDLS_CANCEL_CHAN_SWITCH_REQ", + [MSG_I(TDLS_CANCEL_CHAN_SWITCH_CFM)] = "TDLS_CANCEL_CHAN_SWITCH_CFM", + [MSG_I(TDLS_PEER_PS_IND)] = "TDLS_PEER_PS_IND", + [MSG_I(TDLS_PEER_TRAFFIC_IND_REQ)] = "TDLS_PEER_TRAFFIC_IND_REQ", + [MSG_I(TDLS_PEER_TRAFFIC_IND_CFM)] = "TDLS_PEER_TRAFFIC_IND_CFM", +}; + +#ifdef CONFIG_RWNX_FULLMAC + +static const char *const rwnx_scanuid2str[MSG_I(SCANU_MAX)] = { + [MSG_I(SCANU_START_REQ)] = "SCANU_START_REQ", + [MSG_I(SCANU_START_CFM)] = "SCANU_START_CFM", + [MSG_I(SCANU_JOIN_REQ)] = "SCANU_JOIN_REQ", + [MSG_I(SCANU_JOIN_CFM)] = "SCANU_JOIN_CFM", + [MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND", + [MSG_I(SCANU_FAST_REQ)] = "SCANU_FAST_REQ", + [MSG_I(SCANU_FAST_CFM)] = "SCANU_FAST_CFM", + [MSG_I(SCANU_VENDOR_IE_REQ)] = "SCANU_VENDOR_IE_REQ", + [MSG_I(SCANU_VENDOR_IE_CFM)] = "SCANU_VENDOR_IE_CFM", + [MSG_I(SCANU_START_CFM_ADDTIONAL)] = "SCANU_START_CFM_ADDTIONAL", + [MSG_I(SCANU_CANCEL_REQ)] = "SCANU_CANCEL_REQ", + [MSG_I(SCANU_CANCEL_CFM)] = "SCANU_CANCEL_CFM", +}; + +static const char *const rwnx_meid2str[MSG_I(ME_MAX)] = { + [MSG_I(ME_CONFIG_REQ)] = "ME_CONFIG_REQ", + [MSG_I(ME_CONFIG_CFM)] = "ME_CONFIG_CFM", + [MSG_I(ME_CHAN_CONFIG_REQ)] = "ME_CHAN_CONFIG_REQ", + [MSG_I(ME_CHAN_CONFIG_CFM)] = "ME_CHAN_CONFIG_CFM", + [MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ", + [MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM", + [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND", + [MSG_I(ME_STA_ADD_REQ)] = "ME_STA_ADD_REQ", + [MSG_I(ME_STA_ADD_CFM)] = "ME_STA_ADD_CFM", + [MSG_I(ME_STA_DEL_REQ)] = "ME_STA_DEL_REQ", + [MSG_I(ME_STA_DEL_CFM)] = "ME_STA_DEL_CFM", + [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = "ME_TX_CREDITS_UPDATE_IND", + [MSG_I(ME_RC_STATS_REQ)] = "ME_RC_STATS_REQ", + [MSG_I(ME_RC_STATS_CFM)] = "ME_RC_STATS_CFM", + [MSG_I(ME_RC_SET_RATE_REQ)] = "ME_RC_SET_RATE_REQ", + [MSG_I(ME_TRAFFIC_IND_REQ)] = "ME_TRAFFIC_IND_REQ", + [MSG_I(ME_TRAFFIC_IND_CFM)] = "ME_TRAFFIC_IND_CFM", + [MSG_I(ME_SET_PS_MODE_REQ)] = "ME_SET_PS_MODE_REQ", + [MSG_I(ME_SET_PS_MODE_CFM)] = "ME_SET_PS_MODE_CFM", + [MSG_I(ME_SET_LP_LEVEL_REQ)] = "ME_SET_LP_LEVEL_REQ", + [MSG_I(ME_SET_LP_LEVEL_CFM)] = "ME_SET_LP_LEVEL_CFM", +}; + +static const char *const rwnx_smid2str[MSG_I(SM_MAX)] = { + [MSG_I(SM_CONNECT_REQ)] = "SM_CONNECT_REQ", + [MSG_I(SM_CONNECT_CFM)] = "SM_CONNECT_CFM", + [MSG_I(SM_CONNECT_IND)] = "SM_CONNECT_IND", + [MSG_I(SM_DISCONNECT_REQ)] = "SM_DISCONNECT_REQ", + [MSG_I(SM_DISCONNECT_CFM)] = "SM_DISCONNECT_CFM", + [MSG_I(SM_DISCONNECT_IND)] = "SM_DISCONNECT_IND", + [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = "SM_EXTERNAL_AUTH_REQUIRED_IND", + [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP)] = "SM_EXTERNAL_AUTH_REQUIRED_RSP", + [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM)] = + "SM_EXTERNAL_AUTH_REQUIRED_RSP_CFM", +}; + +static const char *const rwnx_apmid2str[MSG_I(APM_MAX)] = { + [MSG_I(APM_START_REQ)] = "APM_START_REQ", + [MSG_I(APM_START_CFM)] = "APM_START_CFM", + [MSG_I(APM_STOP_REQ)] = "APM_STOP_REQ", + [MSG_I(APM_STOP_CFM)] = "APM_STOP_CFM", + [MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ", + [MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM", + [MSG_I(APM_STOP_CAC_REQ)] = "APM_STOP_CAC_REQ", + [MSG_I(APM_STOP_CAC_CFM)] = "APM_STOP_CAC_CFM", + [MSG_I(APM_SET_BEACON_IE_REQ)] = "APM_SET_BEACON_IE_REQ", + [MSG_I(APM_SET_BEACON_IE_CFM)] = "APM_SET_BEACON_IE_CFM", +}; + +static const char *const rwnx_meshid2str[MSG_I(MESH_MAX)] = { + [MSG_I(MESH_START_REQ)] = "MESH_START_REQ", + [MSG_I(MESH_START_CFM)] = "MESH_START_CFM", + [MSG_I(MESH_STOP_REQ)] = "MESH_STOP_REQ", + [MSG_I(MESH_STOP_CFM)] = "MESH_STOP_CFM", + [MSG_I(MESH_UPDATE_REQ)] = "MESH_UPDATE_REQ", + [MSG_I(MESH_UPDATE_CFM)] = "MESH_UPDATE_CFM", + [MSG_I(MESH_PATH_CREATE_REQ)] = "MESH_PATH_CREATE_REQ", + [MSG_I(MESH_PATH_CREATE_CFM)] = "MESH_PATH_CREATE_CFM", + [MSG_I(MESH_PATH_UPDATE_REQ)] = "MESH_PATH_UPDATE_REQ", + [MSG_I(MESH_PATH_UPDATE_CFM)] = "MESH_PATH_UPDATE_CFM", + [MSG_I(MESH_PROXY_ADD_REQ)] = "MESH_PROXY_ADD_REQ", + [MSG_I(MESH_PEER_UPDATE_IND)] = "MESH_PEER_UPDATE_IND", + [MSG_I(MESH_PATH_UPDATE_IND)] = "MESH_PATH_UPDATE_IND", + [MSG_I(MESH_PROXY_UPDATE_IND)] = "MESH_PROXY_UPDATE_IND", +}; + +#endif /* CONFIG_RWNX_FULLMAC */ + +const char *const *rwnx_id2str[TASK_LAST_EMB + 1] = { + [TASK_MM] = rwnx_mmid2str, [TASK_DBG] = rwnx_dbgid2str, + [TASK_SCAN] = rwnx_scanid2str, [TASK_TDLS] = rwnx_tdlsid2str, +#ifdef CONFIG_RWNX_FULLMAC + [TASK_SCANU] = rwnx_scanuid2str, [TASK_ME] = rwnx_meid2str, + [TASK_SM] = rwnx_smid2str, [TASK_APM] = rwnx_apmid2str, + [TASK_MESH] = rwnx_meshid2str, +#endif +}; diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h new file mode 100644 index 0000000000000..1b4df40f2a912 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_strs.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_strs.h + * + * @brief Miscellaneous debug strings + * + **************************************************************************************** + */ + +#ifndef _RWNX_STRS_H_ +#define _RWNX_STRS_H_ + +#include "lmac_msg.h" + +#define RWNX_ID2STR(tag) \ + ({ \ + const typeof(tag) __tag = (tag); \ + ((MSG_T(__tag) < ARRAY_SIZE(rwnx_id2str)) && \ + (rwnx_id2str[MSG_T(__tag)]) && \ + ((rwnx_id2str[MSG_T(__tag)])[MSG_I(__tag)])) \ + ? (rwnx_id2str[MSG_T(__tag)])[MSG_I(__tag)] \ + : "unknown"; \ + }) + +extern const char *const *rwnx_id2str[TASK_LAST_EMB + 1]; + +#endif /* _RWNX_STRS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c new file mode 100644 index 0000000000000..73e8ab7c8af5c --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_utils.c + * + * @brief IPC utility function declarations + * + **************************************************************************************** + */ + +#include "rwnx_utils.h" +#include "ipc_host.h" +#include "rwnx_defs.h" +#include "rwnx_msg_rx.h" +#include "rwnx_prof.h" +#include "rwnx_rx.h" +#include "rwnx_tx.h" + +int rwnx_init_aic(struct rwnx_hw *rwnx_hw) +{ + RWNX_DBG(RWNX_FN_ENTRY_STR); +#ifdef AICWF_SDIO_SUPPORT + aicwf_sdio_host_init(&rwnx_hw->sdio_env, NULL, NULL, rwnx_hw); +#else + aicwf_usb_host_init(&rwnx_hw->usb_env, NULL, NULL, rwnx_hw); +#endif + rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr); + + rwnx_hw->testmode = get_testmode(); + + return 0; +} diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h new file mode 100644 index 0000000000000..aecd627ff9867 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_utils.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_utils.c + * + * @brief IPC utility function declarations + * + **************************************************************************************** + */ + +#ifndef _RWNX_IPC_UTILS_H_ +#define _RWNX_IPC_UTILS_H_ + +#include "aicwf_debug.h" +#include +#include +#include +#include "lmac_msg.h" +#include "rwnx_compat.h" + +enum rwnx_dev_flag { + RWNX_DEV_RESTARTING, + RWNX_DEV_STACK_RESTARTING, + RWNX_DEV_STARTED, +}; + +struct rwnx_hw; +struct rwnx_sta; + +/** + * struct rwnx_ipc_elem - Generic IPC buffer of fixed size + * + * @addr: Host address of the buffer. + * @dma_addr: DMA address of the buffer. + */ +struct rwnx_ipc_elem { + void *addr; + dma_addr_t dma_addr; +}; + +/** + * struct rwnx_ipc_elem_pool - Generic pool of IPC buffers of fixed size + * + * @nb: Number of buffers currenlty allocated in the pool + * @buf: Array of buffers (size of array is @nb) + * @pool: DMA pool in which buffers have been allocated + */ +struct rwnx_ipc_elem_pool { + int nb; + struct rwnx_ipc_elem *buf; + struct dma_pool *pool; +}; + +/** + * struct rwnx_ipc_elem_var - Generic IPC buffer of variable size + * + * @addr: Host address of the buffer. + * @dma_addr: DMA address of the buffer. + * @size: Size, in bytes, of the buffer + */ +struct rwnx_ipc_elem_var { + void *addr; + dma_addr_t dma_addr; + size_t size; +}; + +/** + * struct rwnx_ipc_dbgdump_elem - IPC buffer for debug dump + * + * @mutex: Mutex to protect access to debug dump + * @buf: IPC buffer + */ +struct rwnx_ipc_dbgdump_elem { + struct mutex mutex; //Mutex to protect access to debug dump + struct rwnx_ipc_elem_var buf; +}; + +/* + * Maximum Length of Radiotap header vendor specific data(in bytes) + */ +#define RADIOTAP_HDR_VEND_MAX_LEN 16 + +/* + * Maximum Radiotap Header Length without vendor specific data (in bytes) + */ +#define RADIOTAP_HDR_MAX_LEN 80 + +/* + * Unsupported HT Frame data length (in bytes) + */ +#define UNSUP_RX_VEC_DATA_LEN 2 + +/** + * struct rwnx_ipc_skb_elem - IPC buffer for SKB element + * + * @skb: Pointer to the skb buffer allocated + * @dma_addr: DMA address of the data buffer fo skb + * + */ +struct rwnx_ipc_skb_elem { + struct sk_buff *skb; + dma_addr_t dma_addr; +}; + +#ifdef CONFIG_RWNX_FULLMAC + +/* Maximum number of rx buffer the fw may use at the same time */ +#define RWNX_RXBUFF_MAX (64 * NX_REMOTE_STA_MAX) + +/** + * struct rwnx_ipc_rxbuf_elems - IPC buffers for RX + * + * @skb: Array of buffer push to FW. + * @idx: Index of the last pushed skb.(Use to find the next free entry quicker) + * + * Note: contrary to softmac version, dma_addr are stored inside skb->cb. + */ +struct rwnx_ipc_rxbuf_elems { + struct sk_buff *skb[RWNX_RXBUFF_MAX]; + int idx; +}; + +#endif /* CONFIG_RWNX_FULLMAC */ +int rwnx_init_aic(struct rwnx_hw *rwnx_hw); + +int get_testmode(void); + +#endif /* _RWNX_IPC_UTILS_H_ */ diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c new file mode 100644 index 0000000000000..4138c7f7d07e4 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_v7.h + * + **************************************************************************************** + */ + +#include "rwnx_v7.h" +#include "hal_desc.h" +#include "reg_access.h" +#include "rwnx_defs.h" +#include "rwnx_irqs.h" + +struct rwnx_v7 { + u8 __iomem *pci_bar0_vaddr; + u8 __iomem *pci_bar1_vaddr; +}; + +static int rwnx_v7_platform_enable(struct rwnx_hw *rwnx_hw) +{ + int ret; + + /* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */ + ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0, "rwnx", + rwnx_hw); + return ret; +} + +static int rwnx_v7_platform_disable(struct rwnx_hw *rwnx_hw) +{ + free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw); + return 0; +} + +static void rwnx_v7_platform_deinit(struct rwnx_plat *rwnx_plat) +{ +#ifdef CONFIG_PCI + struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv; + + pci_disable_device(rwnx_plat->pci_dev); + iounmap(rwnx_v7->pci_bar0_vaddr); + iounmap(rwnx_v7->pci_bar1_vaddr); + pci_release_regions(rwnx_plat->pci_dev); + pci_clear_master(rwnx_plat->pci_dev); + pci_disable_msi(rwnx_plat->pci_dev); +#endif + kfree(rwnx_plat); +} + +static u8 __iomem *rwnx_v7_get_address(struct rwnx_plat *rwnx_plat, + int addr_name, unsigned int offset) +{ + struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv; + + if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name)) + return NULL; + + if (addr_name == RWNX_ADDR_CPU) + return rwnx_v7->pci_bar0_vaddr + offset; + else + return rwnx_v7->pci_bar1_vaddr + offset; +} + +static void rwnx_v7_ack_irq(struct rwnx_plat *rwnx_plat) +{ +} + +static const u32 rwnx_v7_config_reg[] = { + NXMAC_DEBUG_PORT_SEL_ADDR, SYSCTRL_DIAG_CONF_ADDR, + SYSCTRL_PHYDIAG_CONF_ADDR, SYSCTRL_RIUDIAG_CONF_ADDR, + RF_V7_DIAGPORT_CONF1_ADDR, +}; + +static const u32 rwnx_v7_he_config_reg[] = { + SYSCTRL_DIAG_CONF0, + SYSCTRL_DIAG_CONF1, + SYSCTRL_DIAG_CONF2, + SYSCTRL_DIAG_CONF3, +}; + +static int rwnx_v7_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list) +{ + u32 fpga_sign; + + if (!list) + return 0; + + fpga_sign = + RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR); + if (__FPGA_TYPE(fpga_sign) == 0xc0ca) { + *list = rwnx_v7_he_config_reg; + return ARRAY_SIZE(rwnx_v7_he_config_reg); + } + *list = rwnx_v7_config_reg; + return ARRAY_SIZE(rwnx_v7_config_reg); +} + +/** + * rwnx_v7_platform_init - Initialize the DINI platform + * @pci_dev: PCI device + * @rwnx_plat: Location in which to return the allocated platform data + * + * Allocate and initialize a rwnx_plat structure for the dini platform. + * + * Return: 0 on success, or a negative error code. + */ +int rwnx_v7_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat) +{ + struct rwnx_v7 *rwnx_v7; + u16 pci_cmd; + int ret = 0; + + *rwnx_plat = + kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_v7), GFP_KERNEL); + if (!*rwnx_plat) + return -ENOMEM; + + rwnx_v7 = (struct rwnx_v7 *)(*rwnx_plat)->priv; + + /* Hotplug fixups */ + pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd); + pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd); + + ret = pci_enable_device(pci_dev); + if (ret) { + dev_err(&pci_dev->dev, "pci_enable_device failed\n"); + goto out_enable; + } + + pci_set_master(pci_dev); + +#ifdef CONFIG_PCI + if (pci_enable_msi(pci_dev)) { + dev_err(&pci_dev->dev, "pci_enable_msi failed\n"); + goto out_msi; + } +#endif + + rwnx_v7->pci_bar0_vaddr = pci_ioremap_bar(pci_dev, 0); + if (!rwnx_v7->pci_bar0_vaddr) { + dev_err(&pci_dev->dev, "pci_ioremap_bar(%d) failed\n", 0); + ret = -ENOMEM; + goto out_bar0; + } + rwnx_v7->pci_bar1_vaddr = pci_ioremap_bar(pci_dev, 1); + if (!rwnx_v7->pci_bar1_vaddr) { + dev_err(&pci_dev->dev, "pci_ioremap_bar(%d) failed\n", 1); + ret = -ENOMEM; + goto out_bar1; + } + + (*rwnx_plat)->enable = rwnx_v7_platform_enable; + (*rwnx_plat)->disable = rwnx_v7_platform_disable; + (*rwnx_plat)->deinit = rwnx_v7_platform_deinit; + (*rwnx_plat)->get_address = rwnx_v7_get_address; + (*rwnx_plat)->ack_irq = rwnx_v7_ack_irq; + (*rwnx_plat)->get_config_reg = rwnx_v7_get_config_reg; + + return 0; + +out_bar1: + iounmap(rwnx_v7->pci_bar0_vaddr); +out_bar0: +#ifdef CONFIG_PCI + pci_disable_msi(pci_dev); +out_msi: +#endif + pci_release_regions(pci_dev); +#ifdef CONFIG_PCI + pci_clear_master(pci_dev); +#endif + pci_disable_device(pci_dev); +out_enable: + kfree(*rwnx_plat); + return ret; +} diff --git a/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h new file mode 100644 index 0000000000000..709553f96c2a1 --- /dev/null +++ b/drivers/net/wireless/aic/aic8800_fdrv/rwnx_v7.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + **************************************************************************************** + * + * Copyright (C) 2020 AIC semiconductor. + * + * @file rwnx_v7.h + * + **************************************************************************************** + */ + +#ifndef _RWNX_V7_H_ +#define _RWNX_V7_H_ + +#include "rwnx_platform.h" +#include + +int rwnx_v7_platform_init(struct pci_dev *pci_dev, + struct rwnx_plat **rwnx_plat); + +#endif /* _RWNX_V7_H_ */ -- 2.34.1