2024-05-21 17:19:53

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 00/17] wifi: cc33xx: Add driver for new TI CC33xx wireless device family

From: Michael Nemanov <[email protected]>

Hello everyone,

This series adds support for CC33xx which is a new family of WLAN IEEE802.11 a/b/g/n/ax
and BLE 5.4 transceivers by Texas Instruments. These devices are 20MHz single spatial stream
enabling STA (IEEE802.11ax) and AP (IEEE802.11n only) roles as well as both roles simultaneously.
Communication to the CC33xx is done via 4-bit SDIO with two extra GPIOs: Enable and Interrupt.

Data sheet: https://www.ti.com/lit/gpn/cc3301

This driver's architecture is a soft-MAC and derivative of existing wl18xx + wlcore code [1].
It has been tested with the AM335x, AM625x, and i.MX8-MP evaluation kits.

All code passes sparse and checkpatch with very few pragmatic exceptions.

Known gaps to be addressed in following patches:
1. BLE support

Test log:
https://0x0.st/XPUd.log

Change log:
v1:
* Added dt-bindings
* Removed debugfs to ease review
* Fix build issue with CONFIG_CFG80211_CERTIFICATION_ONUS
* Fix multiple build warnings found with Clang 18 and W=12

RFC: https://lore.kernel.org/linux-wireless/[email protected]/


[1] It was considered implementing CC33xx as another user of wlcore but The
differences in HW, host interface, IRQ functionality, Rx/Tx behavior and supported features
were too significant so this was abandoned.

Michael Nemanov
Texas Instruments

Michael Nemanov (17):
Add cc33xx.h, cc33xx_i.h
Add debug.h
Add sdio.c, io.c, io.h
Add cmd.c, cmd.h
Add acx.c, acx.h
Add event.c, event.h
Add boot.c, boot.h
Add main.c
Add rx.c, rx.h
Add tx.c, tx.h
Add init.c, init.h
Add scan.c, scan.h
Add conf.h
Add ps.c, ps.h
Add testmode.c, testmode.h
Add Kconfig, Makefile and integrate into wireless/ti folder
Add ti,cc33xx.yaml

.../bindings/net/wireless/ti,cc33xx.yaml | 60 +
drivers/net/wireless/ti/Kconfig | 1 +
drivers/net/wireless/ti/Makefile | 1 +
drivers/net/wireless/ti/cc33xx/Kconfig | 24 +
drivers/net/wireless/ti/cc33xx/Makefile | 10 +
drivers/net/wireless/ti/cc33xx/acx.c | 1009 +++
drivers/net/wireless/ti/cc33xx/acx.h | 835 +++
drivers/net/wireless/ti/cc33xx/boot.c | 363 +
drivers/net/wireless/ti/cc33xx/boot.h | 24 +
drivers/net/wireless/ti/cc33xx/cc33xx.h | 481 ++
drivers/net/wireless/ti/cc33xx/cc33xx_i.h | 459 ++
drivers/net/wireless/ti/cc33xx/cmd.c | 2033 ++++++
drivers/net/wireless/ti/cc33xx/cmd.h | 700 ++
drivers/net/wireless/ti/cc33xx/conf.h | 1246 ++++
drivers/net/wireless/ti/cc33xx/debug.h | 92 +
drivers/net/wireless/ti/cc33xx/event.c | 389 ++
drivers/net/wireless/ti/cc33xx/event.h | 71 +
drivers/net/wireless/ti/cc33xx/init.c | 236 +
drivers/net/wireless/ti/cc33xx/init.h | 15 +
drivers/net/wireless/ti/cc33xx/io.c | 131 +
drivers/net/wireless/ti/cc33xx/io.h | 26 +
drivers/net/wireless/ti/cc33xx/main.c | 5949 +++++++++++++++++
drivers/net/wireless/ti/cc33xx/ps.c | 117 +
drivers/net/wireless/ti/cc33xx/ps.h | 16 +
drivers/net/wireless/ti/cc33xx/rx.c | 393 ++
drivers/net/wireless/ti/cc33xx/rx.h | 86 +
drivers/net/wireless/ti/cc33xx/scan.c | 756 +++
drivers/net/wireless/ti/cc33xx/scan.h | 364 +
drivers/net/wireless/ti/cc33xx/sdio.c | 581 ++
drivers/net/wireless/ti/cc33xx/testmode.c | 357 +
drivers/net/wireless/ti/cc33xx/testmode.h | 12 +
drivers/net/wireless/ti/cc33xx/tx.c | 1416 ++++
drivers/net/wireless/ti/cc33xx/tx.h | 160 +
33 files changed, 18413 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/wireless/ti,cc33xx.yaml
create mode 100644 drivers/net/wireless/ti/cc33xx/Kconfig
create mode 100644 drivers/net/wireless/ti/cc33xx/Makefile
create mode 100644 drivers/net/wireless/ti/cc33xx/acx.c
create mode 100644 drivers/net/wireless/ti/cc33xx/acx.h
create mode 100644 drivers/net/wireless/ti/cc33xx/boot.c
create mode 100644 drivers/net/wireless/ti/cc33xx/boot.h
create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx.h
create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx_i.h
create mode 100644 drivers/net/wireless/ti/cc33xx/cmd.c
create mode 100644 drivers/net/wireless/ti/cc33xx/cmd.h
create mode 100644 drivers/net/wireless/ti/cc33xx/conf.h
create mode 100644 drivers/net/wireless/ti/cc33xx/debug.h
create mode 100644 drivers/net/wireless/ti/cc33xx/event.c
create mode 100644 drivers/net/wireless/ti/cc33xx/event.h
create mode 100644 drivers/net/wireless/ti/cc33xx/init.c
create mode 100644 drivers/net/wireless/ti/cc33xx/init.h
create mode 100644 drivers/net/wireless/ti/cc33xx/io.c
create mode 100644 drivers/net/wireless/ti/cc33xx/io.h
create mode 100644 drivers/net/wireless/ti/cc33xx/main.c
create mode 100644 drivers/net/wireless/ti/cc33xx/ps.c
create mode 100644 drivers/net/wireless/ti/cc33xx/ps.h
create mode 100644 drivers/net/wireless/ti/cc33xx/rx.c
create mode 100644 drivers/net/wireless/ti/cc33xx/rx.h
create mode 100644 drivers/net/wireless/ti/cc33xx/scan.c
create mode 100644 drivers/net/wireless/ti/cc33xx/scan.h
create mode 100644 drivers/net/wireless/ti/cc33xx/sdio.c
create mode 100644 drivers/net/wireless/ti/cc33xx/testmode.c
create mode 100644 drivers/net/wireless/ti/cc33xx/testmode.h
create mode 100644 drivers/net/wireless/ti/cc33xx/tx.c
create mode 100644 drivers/net/wireless/ti/cc33xx/tx.h


base-commit: 2785ea9673a7305abeea87111849a4e04b0f4626
--
2.25.1



2024-05-21 17:20:00

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 03/17] Add sdio.c, io.c, io.h

From: Michael Nemanov <[email protected]>

sdio.c implements SDIO transport functions. These are bound into
struct cc33xx_if_operations and accessed via io.h in order to abstract
multiple transport interfaces such as SPI in the future.
The CC33xx driver supports the SDIO in-band IRQ option so the IRQ from
the device received here as well.
Unlike wl1xxx products, there is no longer mapping between
HW and SDIO / SPI address space of any kind.
There are only 3 valid addresses for control, data and status
transactions each with a predefined structure.

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/io.c | 131 ++++++
drivers/net/wireless/ti/cc33xx/io.h | 26 ++
drivers/net/wireless/ti/cc33xx/sdio.c | 581 ++++++++++++++++++++++++++
3 files changed, 738 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/io.c
create mode 100644 drivers/net/wireless/ti/cc33xx/io.h
create mode 100644 drivers/net/wireless/ti/cc33xx/sdio.c

diff --git a/drivers/net/wireless/ti/cc33xx/io.c b/drivers/net/wireless/ti/cc33xx/io.c
new file mode 100644
index 000000000000..c8759b63ac2d
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/io.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include "cc33xx.h"
+#include "debug.h"
+#include "io.h"
+#include "tx.h"
+
+bool cc33xx_set_block_size(struct cc33xx *cc)
+{
+ if (cc->if_ops->set_block_size) {
+ cc->if_ops->set_block_size(cc->dev, CC33XX_BUS_BLOCK_SIZE);
+ cc33xx_debug(DEBUG_CC33xx,
+ "Set BLKsize to %d", CC33XX_BUS_BLOCK_SIZE);
+ return true;
+ }
+
+ cc33xx_debug(DEBUG_CC33xx, "Could not set BLKsize");
+ return false;
+}
+
+void cc33xx_disable_interrupts_nosync(struct cc33xx *cc)
+{
+ cc->if_ops->disable_irq(cc->dev);
+}
+
+void cc33xx_irq(void *cookie);
+void cc33xx_enable_interrupts(struct cc33xx *cc)
+{
+ cc->if_ops->enable_irq(cc->dev);
+
+ cc33xx_debug(DEBUG_CC33xx, "IBI_WA: Read core status");
+ cc33xx_irq(cc);
+ cc33xx_debug(DEBUG_CC33xx, "IBI_WA: Core status processed");
+}
+
+void cc33xx_io_reset(struct cc33xx *cc)
+{
+ if (cc->if_ops->reset)
+ cc->if_ops->reset(cc->dev);
+}
+
+void cc33xx_io_init(struct cc33xx *cc)
+{
+ if (cc->if_ops->init)
+ cc->if_ops->init(cc->dev);
+}
+
+/* Raw target IO, address is not translated */
+static int __must_check cc33xx_raw_write(struct cc33xx *cc, int addr,
+ void *buf, size_t len, bool fixed)
+{
+ int ret;
+
+ if (test_bit(CC33XX_FLAG_IO_FAILED, &cc->flags) ||
+ WARN_ON((test_bit(CC33XX_FLAG_IN_ELP, &cc->flags) &&
+ addr != HW_ACCESS_ELP_CTRL_REG)))
+ return -EIO;
+
+ ret = cc->if_ops->write(cc->dev, addr, buf, len, fixed);
+ if (ret && cc->state != CC33XX_STATE_OFF)
+ set_bit(CC33XX_FLAG_IO_FAILED, &cc->flags);
+
+ return ret;
+}
+
+int __must_check cc33xx_raw_read(struct cc33xx *cc, int addr,
+ void *buf, size_t len, bool fixed)
+{
+ int ret;
+
+ if (test_bit(CC33XX_FLAG_IO_FAILED, &cc->flags) ||
+ WARN_ON((test_bit(CC33XX_FLAG_IN_ELP, &cc->flags) &&
+ addr != HW_ACCESS_ELP_CTRL_REG)))
+ return -EIO;
+
+ ret = cc->if_ops->read(cc->dev, addr, buf, len, fixed);
+ if (ret && cc->state != CC33XX_STATE_OFF)
+ set_bit(CC33XX_FLAG_IO_FAILED, &cc->flags);
+
+ return ret;
+}
+
+int __must_check cc33xx_write(struct cc33xx *cc, int addr,
+ void *buf, size_t len, bool fixed)
+{
+ return cc33xx_raw_write(cc, addr, buf, len, fixed);
+}
+
+void claim_core_status_lock(struct cc33xx *cc)
+{
+ /* When accessing core-status data (read or write) the transport lock
+ * should be held.
+ */
+ cc->if_ops->interface_claim(cc->dev);
+}
+
+void release_core_status_lock(struct cc33xx *cc)
+{
+ /* After accessing core-status data (read or write) the transport lock
+ * should be released.
+ */
+ cc->if_ops->interface_release(cc->dev);
+}
+
+void cc33xx_power_off(struct cc33xx *cc)
+{
+ int ret = 0;
+
+ if (!test_bit(CC33XX_FLAG_GPIO_POWER, &cc->flags))
+ return;
+
+ if (cc->if_ops->power)
+ ret = cc->if_ops->power(cc->dev, false);
+ if (!ret)
+ clear_bit(CC33XX_FLAG_GPIO_POWER, &cc->flags);
+}
+
+int cc33xx_power_on(struct cc33xx *cc)
+{
+ int ret = 0;
+
+ if (cc->if_ops->power)
+ ret = cc->if_ops->power(cc->dev, true);
+ if (ret == 0)
+ set_bit(CC33XX_FLAG_GPIO_POWER, &cc->flags);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/cc33xx/io.h b/drivers/net/wireless/ti/cc33xx/io.h
new file mode 100644
index 000000000000..cc5abd428d99
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/io.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __IO_H__
+#define __IO_H__
+
+struct cc33xx;
+
+void cc33xx_disable_interrupts_nosync(struct cc33xx *cc);
+void cc33xx_enable_interrupts(struct cc33xx *cc);
+void cc33xx_io_reset(struct cc33xx *cc);
+void cc33xx_io_init(struct cc33xx *cc);
+int __must_check cc33xx_raw_read(struct cc33xx *cc, int addr,
+ void *buf, size_t len, bool fixed);
+int __must_check cc33xx_write(struct cc33xx *cc, int addr,
+ void *buf, size_t len, bool fixed);
+void claim_core_status_lock(struct cc33xx *cc);
+void release_core_status_lock(struct cc33xx *cc);
+void cc33xx_power_off(struct cc33xx *cc);
+int cc33xx_power_on(struct cc33xx *cc);
+int cc33xx_translate_addr(struct cc33xx *cc, int addr);
+bool cc33xx_set_block_size(struct cc33xx *cc);
+
+#endif /* __IO_H__ */
diff --git a/drivers/net/wireless/ti/cc33xx/sdio.c b/drivers/net/wireless/ti/cc33xx/sdio.c
new file mode 100644
index 000000000000..0335a61df868
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/sdio.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/host.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_irq.h>
+
+#include "cc33xx.h"
+#include "io.h"
+
+#ifndef SDIO_VENDOR_ID_TI
+#define SDIO_VENDOR_ID_TI 0x0097
+#endif
+
+#define SDIO_DEVICE_ID_CC33XX_NO_EFUSE 0x4076
+#define SDIO_DEVICE_ID_TI_CC33XX 0x4077
+
+static bool dump;
+
+struct cc33xx_sdio_glue {
+ struct device *dev;
+ struct platform_device *core;
+};
+
+static const struct sdio_device_id cc33xx_devices[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_CC33XX) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_CC33XX_NO_EFUSE) },
+ {}
+};
+MODULE_DEVICE_TABLE(sdio, cc33xx_devices);
+
+static void cc33xx_sdio_claim(struct device *child)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_claim_host(func);
+}
+
+static void cc33xx_sdio_release(struct device *child)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_release_host(func);
+}
+
+static void cc33xx_sdio_set_block_size(struct device *child,
+ unsigned int blksz)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_claim_host(func);
+ sdio_set_block_size(func, blksz);
+ sdio_release_host(func);
+}
+
+static int __must_check cc33xx_sdio_raw_read(struct device *child, int addr,
+ void *buf, size_t len, bool fixed)
+{
+ int ret;
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_claim_host(func);
+
+ if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
+ ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
+ dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
+ addr, ((u8 *)buf)[0]);
+ } else {
+ if (fixed)
+ ret = sdio_readsb(func, buf, addr, len);
+ else
+ ret = sdio_memcpy_fromio(func, buf, addr, len);
+
+ dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
+ addr, len);
+ }
+
+ sdio_release_host(func);
+
+ if (WARN_ON(ret))
+ dev_err(child->parent, "sdio read failed (%d)\n", ret);
+
+ if (unlikely(dump)) {
+ dev_dbg(glue->dev, "cc33xx_sdio: READ from 0x%04x\n", addr);
+ print_hex_dump(KERN_DEBUG, "cc33xx_sdio: READ ",
+ DUMP_PREFIX_OFFSET, 16, 1, buf, len, false);
+ }
+
+ return ret;
+}
+
+static int __must_check cc33xx_sdio_raw_write(struct device *child, int addr,
+ void *buf, size_t len, bool fixed)
+{
+ int ret;
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_claim_host(func);
+
+ if (unlikely(dump)) {
+ dev_dbg(child->parent, "cc33xx_sdio: WRITE to 0x%04x length 0x%zx (first 64 Bytes):\n",
+ addr, len);
+ print_hex_dump(KERN_DEBUG, "cc33xx_sdio: WRITE ",
+ DUMP_PREFIX_OFFSET, 16, 1, buf,
+ min(len, (size_t)64), false);
+ }
+
+ if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
+ sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
+ dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
+ addr, ((u8 *)buf)[0]);
+ } else {
+ dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
+ addr, len);
+
+ if (fixed)
+ ret = sdio_writesb(func, addr, buf, len);
+ else
+ ret = sdio_memcpy_toio(func, addr, buf, len);
+ }
+
+ sdio_release_host(func);
+
+ if (WARN_ON(ret))
+ dev_err(child->parent, "sdio write failed (%d)\n", ret);
+
+ return ret;
+}
+
+static int cc33xx_sdio_power_on(struct cc33xx_sdio_glue *glue)
+{
+ int ret;
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+ struct mmc_card *card = func->card;
+
+ ret = pm_runtime_get_sync(&card->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&card->dev);
+ dev_err(glue->dev, "%s: failed to get_sync(%d)\n",
+ __func__, ret);
+
+ return ret;
+ }
+
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ sdio_release_host(func);
+
+ return 0;
+}
+
+static int cc33xx_sdio_power_off(struct cc33xx_sdio_glue *glue)
+{
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+ struct mmc_card *card = func->card;
+
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+
+ /* Let runtime PM know the card is powered off */
+ pm_runtime_put(&card->dev);
+ return 0;
+}
+
+static int cc33xx_sdio_set_power(struct device *child, bool enable)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+
+ if (enable)
+ return cc33xx_sdio_power_on(glue);
+ else
+ return cc33xx_sdio_power_off(glue);
+}
+
+/**
+ * inband_irq_handler - Called from the MMC subsystem when the
+ * function's IRQ is signaled.
+ * @func: an SDIO function of the card
+ *
+ * Note that the host is already claimed when handler is invoked.
+ */
+static void inband_irq_handler(struct sdio_func *func)
+{
+ struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+ struct platform_device *pdev = glue->core;
+ struct cc33xx_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+ dev_dbg(glue->dev, "Inband SDIO IRQ");
+
+ if (WARN_ON(!pdev_data->irq_handler))
+ return;
+
+ pdev_data->irq_handler(pdev);
+}
+
+static void cc33xx_enable_async_interrupt(struct sdio_func *func)
+{
+ u8 reg_val;
+ const int CCCR_REG_16_ADDR = 0x16;
+ const int ENABLE_ASYNC_IRQ_BIT = BIT(1);
+
+ reg_val = sdio_f0_readb(func, CCCR_REG_16_ADDR, NULL);
+ reg_val |= ENABLE_ASYNC_IRQ_BIT;
+ sdio_f0_writeb(func, reg_val, CCCR_REG_16_ADDR, NULL);
+}
+
+static void cc33xx_sdio_enable_irq(struct device *child)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_claim_host(func);
+ cc33xx_enable_async_interrupt(func);
+ sdio_claim_irq(func, inband_irq_handler);
+ sdio_release_host(func);
+}
+
+static void cc33xx_sdio_disable_irq(struct device *child)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct sdio_func *func = dev_to_sdio_func(glue->dev);
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+}
+
+static void cc33xx_enable_line_irq(struct device *child)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct platform_device *pdev = glue->core;
+ struct cc33xx_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+ enable_irq(pdev_data->gpio_irq_num);
+}
+
+static void cc33xx_disable_line_irq(struct device *child)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct platform_device *pdev = glue->core;
+ struct cc33xx_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+ disable_irq_nosync(pdev_data->gpio_irq_num);
+}
+
+static void cc33xx_set_irq_handler(struct device *child, void *handler)
+{
+ struct cc33xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+ struct platform_device *pdev = glue->core;
+ struct cc33xx_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+ pdev_data->irq_handler = handler;
+}
+
+static struct cc33xx_if_operations sdio_ops_gpio_irq = {
+ .interface_claim = cc33xx_sdio_claim,
+ .interface_release = cc33xx_sdio_release,
+ .read = cc33xx_sdio_raw_read,
+ .write = cc33xx_sdio_raw_write,
+ .power = cc33xx_sdio_set_power,
+ .set_block_size = cc33xx_sdio_set_block_size,
+ .set_irq_handler = cc33xx_set_irq_handler,
+ .disable_irq = cc33xx_disable_line_irq,
+ .enable_irq = cc33xx_enable_line_irq,
+};
+
+static struct cc33xx_if_operations sdio_ops_inband_irq = {
+ .interface_claim = cc33xx_sdio_claim,
+ .interface_release = cc33xx_sdio_release,
+ .read = cc33xx_sdio_raw_read,
+ .write = cc33xx_sdio_raw_write,
+ .power = cc33xx_sdio_set_power,
+ .set_block_size = cc33xx_sdio_set_block_size,
+ .set_irq_handler = cc33xx_set_irq_handler,
+ .disable_irq = cc33xx_sdio_disable_irq,
+ .enable_irq = cc33xx_sdio_enable_irq,
+};
+
+#ifdef CONFIG_OF
+static const struct cc33xx_family_data cc33xx_data = {
+ .name = "cc33xx",
+ .cfg_name = "ti-connectivity/cc33xx-conf.bin",
+ .nvs_name = "ti-connectivity/cc33xx-nvs.bin",
+};
+
+static const struct of_device_id cc33xx_sdio_of_match_table[] = {
+ { .compatible = "ti,cc3300", .data = &cc33xx_data },
+ { .compatible = "ti,cc3301", .data = &cc33xx_data },
+ { .compatible = "ti,cc3350", .data = &cc33xx_data },
+ { .compatible = "ti,cc3351", .data = &cc33xx_data },
+ { }
+};
+
+static int cc33xx_probe_of(struct device *dev, int *irq, int *wakeirq,
+ struct cc33xx_platdev_data *pdev_data)
+{
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *of_id;
+
+ of_id = of_match_node(cc33xx_sdio_of_match_table, np);
+ if (!of_id)
+ return -ENODEV;
+
+ pdev_data->family = of_id->data;
+
+ *irq = irq_of_parse_and_map(np, 0);
+
+ *wakeirq = irq_of_parse_and_map(np, 1);
+
+ return 0;
+}
+#else
+static int cc33xx_probe_of(struct device *dev, int *irq, int *wakeirq,
+ struct cc33xx_platdev_data *pdev_data)
+{
+ return -ENODATA;
+}
+#endif /* CONFIG_OF */
+
+static irqreturn_t gpio_irq_hard_handler(int irq, void *cookie)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t gpio_irq_thread_handler(int irq, void *cookie)
+{
+ struct sdio_func *func = cookie;
+ struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+ struct platform_device *pdev = glue->core;
+ struct cc33xx_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+ if (WARN_ON(!pdev_data->irq_handler))
+ return IRQ_HANDLED;
+
+ pdev_data->irq_handler(pdev);
+
+ return IRQ_HANDLED;
+}
+
+static int sdio_cc33xx_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct cc33xx_platdev_data *pdev_data;
+ struct cc33xx_sdio_glue *glue;
+ struct resource res[1];
+ mmc_pm_flag_t mmcflags;
+ int ret = -ENOMEM;
+ int gpio_irq, wakeirq, irq_flags;
+ const char *chip_family;
+
+ /* We are only able to handle the wlan function */
+ if (func->num != 0x02)
+ return -ENODEV;
+
+ pdev_data = devm_kzalloc(&func->dev, sizeof(*pdev_data), GFP_KERNEL);
+ if (!pdev_data)
+ return -ENOMEM;
+
+ glue = devm_kzalloc(&func->dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
+
+ glue->dev = &func->dev;
+
+ /* Grab access to FN0 for ELP reg. */
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+ /* Use block mode for transferring over one block size of data */
+ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+
+ ret = cc33xx_probe_of(&func->dev, &gpio_irq, &wakeirq, pdev_data);
+ if (ret)
+ goto out;
+
+ /* if sdio can keep power while host is suspended, enable wow */
+ mmcflags = sdio_get_host_pm_caps(func);
+ dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
+
+ sdio_set_drvdata(func, glue);
+
+ /* Tell PM core that we don't need the card to be powered now */
+ pm_runtime_put_noidle(&func->dev);
+
+ chip_family = "cc33xx";
+
+ glue->core = platform_device_alloc(chip_family, PLATFORM_DEVID_AUTO);
+ if (!glue->core) {
+ dev_err(glue->dev, "can't allocate platform_device");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ glue->core->dev.parent = &func->dev;
+
+ if (gpio_irq) {
+ dev_info(glue->dev, "Using GPIO as IRQ\n");
+
+ irq_flags = irqd_get_trigger_type(irq_get_irq_data(gpio_irq));
+
+ irq_set_status_flags(gpio_irq, IRQ_NOAUTOEN);
+
+ if (irq_flags & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW))
+ irq_flags |= IRQF_ONESHOT;
+
+ ret = request_threaded_irq(gpio_irq, gpio_irq_hard_handler,
+ gpio_irq_thread_handler,
+ irq_flags, glue->core->name, func);
+ if (ret) {
+ dev_err(glue->dev, "can't register GPIO IRQ handler\n");
+ goto out_dev_put;
+ }
+
+ pdev_data->gpio_irq_num = gpio_irq;
+
+ if ((mmcflags & MMC_PM_KEEP_POWER) &&
+ (enable_irq_wake(gpio_irq) == 0))
+ pdev_data->pwr_in_suspend = true;
+
+ pdev_data->if_ops = &sdio_ops_gpio_irq;
+ } else {
+ dev_info(glue->dev, "Using SDIO in-band IRQ\n");
+
+ pdev_data->if_ops = &sdio_ops_inband_irq;
+ }
+
+ if (wakeirq > 0) {
+ res[0].start = wakeirq;
+ res[0].flags = IORESOURCE_IRQ |
+ irqd_get_trigger_type(irq_get_irq_data(wakeirq));
+ res[0].name = "wakeirq";
+
+ ret = platform_device_add_resources(glue->core, res, 1);
+ if (ret) {
+ dev_err(glue->dev, "can't add resources\n");
+ goto out_dev_put;
+ }
+ }
+
+ ret = platform_device_add_data(glue->core, pdev_data,
+ sizeof(*pdev_data));
+ if (ret) {
+ dev_err(glue->dev, "can't add platform data\n");
+ goto out_dev_put;
+ }
+
+ ret = platform_device_add(glue->core);
+ if (ret) {
+ dev_err(glue->dev, "can't add platform device\n");
+ goto out_dev_put;
+ }
+ return 0;
+
+out_dev_put:
+ platform_device_put(glue->core);
+
+out:
+ return ret;
+}
+
+static void sdio_cc33xx_remove(struct sdio_func *func)
+{
+ struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+ struct platform_device *pdev = glue->core;
+ struct cc33xx_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
+
+ /* Undo decrement done above in sdio_cc33xx_probe */
+ pm_runtime_get_noresume(&func->dev);
+
+ platform_device_unregister(glue->core);
+
+ if (pdev_data->gpio_irq_num) {
+ free_irq(pdev_data->gpio_irq_num, func);
+ if (pdev_data->pwr_in_suspend)
+ disable_irq_wake(pdev_data->gpio_irq_num);
+ } else {
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+ }
+}
+
+#ifdef CONFIG_PM
+static int cc33xx_suspend(struct device *dev)
+{
+ /* Tell MMC/SDIO core it's OK to power down the card
+ * (if it isn't already), but not to remove it completely
+ */
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct cc33xx_sdio_glue *glue = sdio_get_drvdata(func);
+ struct cc33xx *cc = platform_get_drvdata(glue->core);
+ mmc_pm_flag_t sdio_flags;
+ int ret = 0;
+
+ if (!cc) {
+ dev_err(dev, "no wilink module was probed\n");
+ goto out;
+ }
+
+ dev_dbg(dev, "cc33xx suspend. keep_device_power: %d\n",
+ cc->keep_device_power);
+
+ if (cc->keep_device_power) {
+ sdio_flags = sdio_get_host_pm_caps(func);
+
+ if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+ dev_err(dev, "can't keep power while host is suspended\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* keep power while host suspended */
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret) {
+ dev_err(dev, "error while trying to keep power\n");
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static int cc33xx_resume(struct device *dev)
+{
+ dev_dbg(dev, "cc33xx resume\n");
+
+ return 0;
+}
+
+static const struct dev_pm_ops cc33xx_sdio_pm_ops = {
+ .suspend = cc33xx_suspend,
+ .resume = cc33xx_resume,
+};
+
+static struct sdio_driver cc33xx_sdio_driver = {
+ .name = "cc33xx_sdio",
+ .id_table = cc33xx_devices,
+ .probe = sdio_cc33xx_probe,
+ .remove = sdio_cc33xx_remove,
+ .drv = {
+ .pm = &cc33xx_sdio_pm_ops,
+ },
+};
+#else
+static struct sdio_driver cc33xx_sdio_driver = {
+ .name = "cc33xx_sdio",
+ .id_table = cc33xx_devices,
+ .probe = sdio_cc33xx_probe,
+ .remove = sdio_cc33xx_remove,
+};
+#endif /* CONFIG_PM */
+
+static int __init sdio_cc33xx_init(void)
+{
+ return sdio_register_driver(&cc33xx_sdio_driver);
+}
+
+static void __exit sdio_cc33xx_exit(void)
+{
+ sdio_unregister_driver(&cc33xx_sdio_driver);
+}
+
+module_init(sdio_cc33xx_init);
+module_exit(sdio_cc33xx_exit);
+
+module_param(dump, bool, 0600);
+MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SDIO transport for Texas Instruments CC33xx WLAN driver");
+MODULE_AUTHOR("Michael Nemanov <[email protected]>");
+MODULE_AUTHOR("Sabeeh Khan <[email protected]>");
--
2.25.1


2024-05-21 17:20:30

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 07/17] Add boot.c, boot.h

From: Michael Nemanov <[email protected]>

Implements FW download for CC33xx. The FW comes in 2 parts - a 2nd stage
bootloader (cc33xx_2nd_loader.bin) and the actual FW (cc33xx_fw.bin).
Each file is requested from user space, and transferred to device
chunk by chunk. A dedicated IRQ is excepted after each stage
(Device power-on -> 2nd stage loader -> FW). This logic is implemnted in
cc33xx_init_fw.

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/boot.c | 363 ++++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/boot.h | 24 ++
2 files changed, 387 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/boot.c
create mode 100644 drivers/net/wireless/ti/cc33xx/boot.h

diff --git a/drivers/net/wireless/ti/cc33xx/boot.c b/drivers/net/wireless/ti/cc33xx/boot.c
new file mode 100644
index 000000000000..a0be54c7fbb7
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/boot.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/firmware.h>
+
+#include "boot.h"
+#include "cmd.h"
+#include "debug.h"
+#include "init.h"
+#include "io.h"
+
+#define CC33XX_BOOT_TIMEOUT 2000
+
+struct hwinfo_bitmap {
+ u32 disable_5g : 1u;
+ u32 disable_6g : 1u;
+ u32 disable_ble : 1u;
+ u32 disable_ble_m0plus : 1u;
+ u32 disable_m33 : 1u;
+ u64 udi : 64u;
+ u32 pg_version : 4u;
+ u32 metal_version : 4u;
+ u32 boot_rom_version : 4u;
+ u32 m3_rom_version : 4u;
+ u32 fuse_rom_structure_version : 4u;
+ u64 mac_address : 48u;
+ u32 device_part_number : 6u;
+ u32 package_type : 4u;
+ u32 fw_rollback_protection_1 : 32u;
+ u32 fw_rollback_protection_2 : 32u;
+ u32 fw_rollback_protection_3 : 32u;
+ u32 reserved : 13u;
+} /* Aligned with boot code, must not be __packed */;
+
+union hw_info {
+ struct hwinfo_bitmap bitmap;
+ u8 bytes[sizeof(struct hwinfo_bitmap)];
+};
+
+/* Called from threaded irq context */
+void cc33xx_handle_boot_irqs(struct cc33xx *cc, u32 pending_interrupts)
+{
+ if (WARN_ON(!cc->fw_download))
+ return;
+
+ cc33xx_debug(DEBUG_BOOT, "BOOT IRQs: 0x%x", pending_interrupts);
+
+ atomic_or(pending_interrupts, &cc->fw_download->pending_irqs);
+ complete(&cc->fw_download->wait_on_irq);
+}
+
+static u8 *fetch_container(struct cc33xx *cc, const char *container_name,
+ size_t *container_len)
+{
+ u8 *container_data = NULL;
+ const struct firmware *container;
+ int ret;
+
+ ret = request_firmware(&container, container_name, cc->dev);
+
+ if (ret < 0) {
+ cc33xx_error("could not get container %s: (%d)",
+ container_name, ret);
+ return NULL;
+ }
+
+ if (container->size % 4) {
+ cc33xx_error("container size is not word-aligned: %zu",
+ container->size);
+ goto out;
+ }
+
+ *container_len = container->size;
+ container_data = vmalloc(container->size);
+
+ if (!container_data) {
+ cc33xx_error("could not allocate memory for the container");
+ goto out;
+ }
+
+ memcpy(container_data, container->data, container->size);
+
+out:
+ release_firmware(container);
+ return container_data;
+}
+
+static int cc33xx_set_power_on(struct cc33xx *cc)
+{
+ int ret;
+
+ msleep(CC33XX_PRE_POWER_ON_SLEEP);
+ ret = cc33xx_power_on(cc);
+ if (ret < 0)
+ goto out;
+ msleep(CC33XX_POWER_ON_SLEEP);
+ cc33xx_io_reset(cc);
+ cc33xx_io_init(cc);
+
+out:
+ return ret;
+}
+
+static int cc33xx_chip_wakeup(struct cc33xx *cc)
+{
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_BOOT, "Chip wakeup");
+
+ ret = cc33xx_set_power_on(cc);
+ if (ret < 0)
+ goto out;
+
+ if (!cc33xx_set_block_size(cc))
+ cc->quirks &= ~CC33XX_QUIRK_TX_BLOCKSIZE_ALIGN;
+
+out:
+ return ret;
+}
+
+static int wait_for_boot_irq(struct cc33xx *cc, u32 boot_irq_mask,
+ unsigned long timeout)
+{
+ int ret;
+ u32 pending_irqs;
+ struct cc33xx_fw_download *fw_download;
+
+ fw_download = cc->fw_download;
+
+ ret = wait_for_completion_interruptible_timeout(&fw_download->wait_on_irq,
+ msecs_to_jiffies(timeout));
+
+ /* Fetch pending IRQs while clearing them in fw_download */
+ pending_irqs = atomic_fetch_and(0, &fw_download->pending_irqs);
+ pending_irqs &= ~HINT_COMMAND_COMPLETE;
+
+ reinit_completion(&fw_download->wait_on_irq);
+
+ if (ret == 0) {
+ cc33xx_error("boot IRQ timeout");
+ return -1;
+ } else if (ret < 0) {
+ cc33xx_error("boot IRQ completion error %d", ret);
+ return -2;
+ }
+
+ if (boot_irq_mask != pending_irqs) {
+ cc33xx_error("Unexpected IRQ received @ boot: 0x%x",
+ pending_irqs);
+ return -3;
+ }
+
+ return 0;
+}
+
+static int download_container(struct cc33xx *cc, u8 *container, size_t len)
+{
+ int ret = 0;
+ u8 *current_transfer;
+ size_t current_transfer_size;
+ u8 *const container_end = container + len;
+ size_t max_transfer_size = cc->fw_download->max_transfer_size;
+ bool is_last_transfer;
+
+ current_transfer = container;
+
+ while (current_transfer < container_end) {
+ current_transfer_size = container_end - current_transfer;
+ current_transfer_size =
+ min(current_transfer_size, max_transfer_size);
+
+ is_last_transfer = (current_transfer + current_transfer_size >= container_end);
+
+ ret = cmd_download_container_chunk(cc,
+ current_transfer,
+ current_transfer_size,
+ is_last_transfer);
+
+ current_transfer += current_transfer_size;
+
+ if (ret < 0) {
+ cc33xx_error("Chunk transfer failed");
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int container_download_and_wait(struct cc33xx *cc,
+ const char *container_name,
+ const u32 irq_wait_mask)
+{
+ int ret = -1;
+ u8 *container_data;
+ size_t container_len;
+
+ cc33xx_debug(DEBUG_BOOT,
+ "Downloading %s to device", container_name);
+
+ container_data = fetch_container(cc, container_name, &container_len);
+ if (!container_data)
+ return ret;
+
+ ret = download_container(cc, container_data, container_len);
+ if (ret < 0) {
+ cc33xx_error("Transfer error while downloading %s",
+ container_name);
+ goto out;
+ }
+
+ ret = wait_for_boot_irq(cc, irq_wait_mask, CC33XX_BOOT_TIMEOUT);
+
+ if (ret < 0) {
+ cc33xx_error("%s boot signal timeout", container_name);
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_BOOT, "%s loaded successfully", container_name);
+ ret = 0;
+
+out:
+ vfree(container_data);
+ return ret;
+}
+
+static int fw_download_alloc(struct cc33xx *cc)
+{
+ if (WARN_ON(cc->fw_download))
+ return -EFAULT;
+
+ cc->fw_download = kzalloc(sizeof(*cc->fw_download), GFP_KERNEL);
+ if (!cc->fw_download)
+ return -ENOMEM;
+
+ init_completion(&cc->fw_download->wait_on_irq);
+
+ return 0;
+}
+
+static void fw_download_free(struct cc33xx *cc)
+{
+ if (WARN_ON(!cc->fw_download))
+ return;
+
+ kfree(cc->fw_download);
+ cc->fw_download = NULL;
+}
+
+static int get_device_info(struct cc33xx *cc)
+{
+ int ret;
+ union hw_info hw_info;
+ u64 mac_address;
+
+ ret = cmd_get_device_info(cc, hw_info.bytes, sizeof(hw_info.bytes));
+ if (ret < 0)
+ return ret;
+
+ cc33xx_debug(DEBUG_BOOT,
+ "CC33XX device info: PG version: %d, Metal version: %d, Boot ROM version: %d, M3 ROM version: %d, MAC address: 0x%llx, Device part number: %d",
+ hw_info.bitmap.pg_version, hw_info.bitmap.metal_version,
+ hw_info.bitmap.boot_rom_version,
+ hw_info.bitmap.m3_rom_version,
+ (u64)hw_info.bitmap.mac_address,
+ hw_info.bitmap.device_part_number);
+
+ cc->fw_download->max_transfer_size = 640;
+
+ mac_address = hw_info.bitmap.mac_address;
+
+ cc->fuse_rom_structure_version = hw_info.bitmap.fuse_rom_structure_version;
+ cc->pg_version = hw_info.bitmap.pg_version;
+ cc->device_part_number = hw_info.bitmap.device_part_number;
+ cc->disable_5g = hw_info.bitmap.disable_5g;
+ cc->disable_6g = hw_info.bitmap.disable_6g;
+
+ cc->efuse_mac_address[5] = (u8)(mac_address);
+ cc->efuse_mac_address[4] = (u8)(mac_address >> 8);
+ cc->efuse_mac_address[3] = (u8)(mac_address >> 16);
+ cc->efuse_mac_address[2] = (u8)(mac_address >> 24);
+ cc->efuse_mac_address[1] = (u8)(mac_address >> 32);
+ cc->efuse_mac_address[0] = (u8)(mac_address >> 40);
+
+ return 0;
+}
+
+int cc33xx_init_fw(struct cc33xx *cc)
+{
+ int ret;
+
+ cc->max_cmd_size = CC33XX_CMD_MAX_SIZE;
+
+ ret = fw_download_alloc(cc);
+ if (ret < 0)
+ return ret;
+
+ reinit_completion(&cc->fw_download->wait_on_irq);
+
+ ret = cc33xx_chip_wakeup(cc);
+ if (ret < 0)
+ goto power_off;
+
+ cc33xx_enable_interrupts(cc);
+
+ ret = wait_for_boot_irq(cc, HINT_ROM_LOADER_INIT_COMPLETE,
+ CC33XX_BOOT_TIMEOUT);
+ if (ret < 0)
+ goto disable_irq;
+
+ ret = get_device_info(cc);
+ if (ret < 0)
+ goto disable_irq;
+
+ ret = container_download_and_wait(cc, SECOND_LOADER_NAME,
+ HINT_SECOND_LOADER_INIT_COMPLETE);
+ if (ret < 0)
+ goto disable_irq;
+
+ ret = container_download_and_wait(cc, FW_NAME,
+ HINT_FW_WAKEUP_COMPLETE);
+ if (ret < 0)
+ goto disable_irq;
+
+ ret = cc33xx_download_ini_params_and_wait(cc);
+
+ if (ret < 0)
+ goto disable_irq;
+
+ ret = wait_for_boot_irq(cc, HINT_FW_INIT_COMPLETE, CC33XX_BOOT_TIMEOUT);
+
+ if (ret < 0)
+ goto disable_irq;
+
+ ret = cc33xx_hw_init(cc);
+ if (ret < 0)
+ goto disable_irq;
+
+ /* Now we know if 11a is supported (info from the INI File), so disable
+ * 11a channels if not supported
+ */
+ cc->enable_11a = cc->conf.core.enable_5ghz;
+
+ cc33xx_debug(DEBUG_MAC80211, "11a is %ssupported",
+ cc->enable_11a ? "" : "not ");
+
+ cc->state = CC33XX_STATE_ON;
+ ret = 0;
+ goto out;
+
+disable_irq:
+ cc33xx_disable_interrupts_nosync(cc);
+
+power_off:
+ cc33xx_power_off(cc);
+
+out:
+ fw_download_free(cc);
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/cc33xx/boot.h b/drivers/net/wireless/ti/cc33xx/boot.h
new file mode 100644
index 000000000000..d5b7763dcd0f
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/boot.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __BOOT_H__
+#define __BOOT_H__
+
+#include "cc33xx.h"
+
+int cc33xx_init_fw(struct cc33xx *cc);
+
+void cc33xx_handle_boot_irqs(struct cc33xx *cc, u32 pending_interrupts);
+
+#define SECOND_LOADER_NAME "ti-connectivity/cc33xx_2nd_loader.bin"
+#define FW_NAME "ti-connectivity/cc33xx_fw.bin"
+
+struct cc33xx_fw_download {
+ atomic_t pending_irqs;
+ struct completion wait_on_irq;
+ size_t max_transfer_size;
+};
+
+#endif /* __BOOT_H__ */
--
2.25.1


2024-05-21 17:20:50

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 05/17] Add acx.c, acx.h

From: Michael Nemanov <[email protected]>

These file contain various WLAN-oriented APIs

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/acx.c | 1009 ++++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/acx.h | 835 +++++++++++++++++++++
2 files changed, 1844 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/acx.c
create mode 100644 drivers/net/wireless/ti/cc33xx/acx.h

diff --git a/drivers/net/wireless/ti/cc33xx/acx.c b/drivers/net/wireless/ti/cc33xx/acx.c
new file mode 100644
index 000000000000..1b338b031c51
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/acx.c
@@ -0,0 +1,1009 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include "acx.h"
+
+int cc33xx_acx_clear_statistics(struct cc33xx *cc)
+{
+ struct acx_header *acx;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_ACX, "acx clear statistics");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = cc33xx_cmd_configure(cc, ACX_CLEAR_STATISTICS, acx, sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("failed to clear firmware statistics: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int cc33xx_acx_wake_up_conditions(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 wake_up_event, u8 listen_interval)
+{
+ struct acx_wake_up_condition *wake_up;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX,
+ "acx wake up conditions (wake_up_event %d listen_interval %d)",
+ wake_up_event, listen_interval);
+
+ wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL);
+ if (!wake_up) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ wake_up->wake_up_event = wake_up_event;
+ wake_up->listen_interval = listen_interval;
+
+ ret = cc33xx_cmd_configure(cc, WAKE_UP_CONDITIONS_CFG,
+ wake_up, sizeof(*wake_up));
+ if (ret < 0) {
+ cc33xx_warning("could not set wake up conditions: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(wake_up);
+ return ret;
+}
+
+int cc33xx_acx_sleep_auth(struct cc33xx *cc, u8 sleep_auth)
+{
+ struct acx_sleep_auth *auth;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx sleep auth %d", sleep_auth);
+
+ auth = kzalloc(sizeof(*auth), GFP_KERNEL);
+ if (!auth) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ auth->sleep_auth = sleep_auth;
+
+ ret = cc33xx_cmd_configure(cc, ACX_SLEEP_AUTH, auth, sizeof(*auth));
+ if (ret < 0) {
+ cc33xx_error("could not configure sleep_auth to %d: %d",
+ sleep_auth, ret);
+ goto out;
+ }
+
+ cc->sleep_auth = sleep_auth;
+out:
+ kfree(auth);
+ return ret;
+}
+
+int cc33xx_ble_enable(struct cc33xx *cc, u8 ble_enable)
+{
+ struct debug_header *buf;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "ble enable");
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = cc33xx_cmd_debug(cc, BLE_ENABLE, buf, sizeof(*buf));
+ if (ret < 0) {
+ cc33xx_error("could not enable ble");
+ goto out;
+ }
+
+ cc->ble_enable = 1;
+out:
+ kfree(buf);
+ return ret;
+}
+
+int cc33xx_acx_tx_power(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ int power)
+{
+ struct acx_tx_power_cfg *acx;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx TX_POWER_CFG %d", power);
+
+ if (power < CC33XX_MIN_TXPWR) {
+ cc33xx_warning("Configured Tx power %d dBm. Increasing to minimum %d dBm",
+ power, CC33XX_MIN_TXPWR);
+ power = CC33XX_MIN_TXPWR;
+ } else if (power > CC33XX_MAX_TXPWR) {
+ cc33xx_warning("Configured Tx power %d dBm is bigger than upper limit: %d dBm. Attenuating to max limit",
+ power, CC33XX_MAX_TXPWR);
+ power = CC33XX_MAX_TXPWR;
+ }
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->role_id = wlvif->role_id;
+ acx->tx_power = power;
+
+ ret = cc33xx_cmd_configure(cc, TX_POWER_CFG, acx, sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("Configure of tx power failed: %d", ret);
+ goto out;
+ }
+
+ wlvif->power_level = power;
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+static int cc33xx_acx_mem_map(struct cc33xx *cc,
+ struct acx_header *memeroy_map, size_t len)
+{
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx mem map");
+
+ ret = cc33xx_cmd_interrogate(cc, MEM_MAP_INTR, memeroy_map,
+ sizeof(struct acx_header), len);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cc33xx_acx_get_fw_versions(struct cc33xx *cc,
+ struct cc33xx_acx_fw_versions *get_fw_versions,
+ size_t len)
+{
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx get FW versions");
+
+ ret = cc33xx_cmd_interrogate(cc, GET_FW_VERSIONS_INTR, get_fw_versions,
+ sizeof(struct cc33xx_acx_fw_versions), len);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+int cc33xx_acx_slot(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum acx_slot_type slot_time)
+{
+ struct acx_slot *slot;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx slot");
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ slot->role_id = wlvif->role_id;
+ slot->slot_time = slot_time;
+ ret = cc33xx_cmd_configure(cc, SLOT_CFG, slot, sizeof(*slot));
+
+ if (ret < 0) {
+ cc33xx_warning("failed to set slot time: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(slot);
+ return ret;
+}
+
+int cc33xx_acx_group_address_tbl(struct cc33xx *cc, bool enable, void *mc_list, u32 mc_list_len)
+{
+ struct acx_dot11_grp_addr_tbl *acx;
+ int ret;
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_ACX, "acx group address tbl");
+
+ acx->enabled = enable;
+ acx->num_groups = mc_list_len;
+ memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
+
+ ret = cc33xx_cmd_configure(cc, DOT11_GROUP_ADDRESS_TBL,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("failed to set group addr table: %d", ret);
+ goto out;
+ }
+out:
+ kfree(acx);
+ return ret;
+}
+
+int cc33xx_acx_beacon_filter_opt(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ bool enable_filter)
+{
+ struct acx_beacon_filter_option *beacon_filter = NULL;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_ACX, "acx beacon filter opt enable=%d",
+ enable_filter);
+
+ if (enable_filter &&
+ cc->conf.host_conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED)
+ goto out;
+
+ beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL);
+ if (!beacon_filter) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ beacon_filter->role_id = wlvif->role_id;
+ beacon_filter->enable = enable_filter;
+
+ /* When set to zero, and the filter is enabled, beacons
+ * without the unicast TIM bit set are dropped.
+ */
+ beacon_filter->max_num_beacons = 0;
+
+ ret = cc33xx_cmd_configure(cc, BEACON_FILTER_OPT,
+ beacon_filter, sizeof(*beacon_filter));
+ if (ret < 0) {
+ cc33xx_warning("failed to set beacon filter opt: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(beacon_filter);
+ return ret;
+}
+
+int cc33xx_acx_beacon_filter_table(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct acx_beacon_filter_ie_table *ie_table;
+ struct conf_bcn_filt_rule bcn_filt_ie[32];
+ struct conf_bcn_filt_rule *p_bcn_filt_ie;
+ int i, idx = 0;
+ int ret;
+ bool vendor_spec = false;
+
+ cc33xx_debug(DEBUG_ACX, "acx beacon filter table");
+
+ ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL);
+ if (!ie_table) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* configure default beacon pass-through rules */
+ ie_table->role_id = wlvif->role_id;
+ ie_table->num_ie = 0;
+ p_bcn_filt_ie = &cc->conf.host_conf.conn.bcn_filt_ie0;
+ memcpy(bcn_filt_ie, p_bcn_filt_ie, 32 * sizeof(struct conf_bcn_filt_rule));
+ for (i = 0; i < cc->conf.host_conf.conn.bcn_filt_ie_count; i++) {
+ struct conf_bcn_filt_rule *r = &bcn_filt_ie[i];
+
+ ie_table->table[idx++] = r->ie;
+ ie_table->table[idx++] = r->rule;
+
+ if (r->ie == WLAN_EID_VENDOR_SPECIFIC) {
+ /* only one vendor specific ie allowed */
+ if (vendor_spec)
+ continue;
+
+ /* for vendor specific rules configure the
+ * additional fields
+ */
+ memcpy(&ie_table->table[idx], r->oui,
+ CONF_BCN_IE_OUI_LEN);
+ idx += CONF_BCN_IE_OUI_LEN;
+ ie_table->table[idx++] = r->type;
+ memcpy(&ie_table->table[idx], r->version,
+ CONF_BCN_IE_VER_LEN);
+ idx += CONF_BCN_IE_VER_LEN;
+ vendor_spec = true;
+ }
+
+ ie_table->num_ie++;
+ }
+
+ ret = cc33xx_cmd_configure(cc, BEACON_FILTER_TABLE,
+ ie_table, sizeof(*ie_table));
+ if (ret < 0) {
+ cc33xx_warning("failed to set beacon filter table: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(ie_table);
+ return ret;
+}
+
+int cc33xx_assoc_info_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct ieee80211_sta *sta, u16 aid)
+{
+ struct assoc_info_cfg *cfg;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx aid");
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cfg->role_id = wlvif->role_id;
+ cfg->aid = cpu_to_le16(aid);
+ cfg->wmm_enabled = wlvif->wmm_enabled;
+
+ cfg->nontransmitted = wlvif->nontransmitted;
+ cfg->bssid_index = wlvif->bssid_index;
+ cfg->bssid_indicator = wlvif->bssid_indicator;
+ cfg->ht_supported = sta->deflink.ht_cap.ht_supported;
+ cfg->vht_supported = sta->deflink.vht_cap.vht_supported;
+ cfg->has_he = sta->deflink.he_cap.has_he;
+ memcpy(cfg->transmitter_bssid, wlvif->transmitter_bssid, ETH_ALEN);
+ ret = cc33xx_cmd_configure(cc, ASSOC_INFO_CFG, cfg, sizeof(*cfg));
+ if (ret < 0) {
+ cc33xx_warning("failed to set aid: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(cfg);
+ return ret;
+}
+
+int cc33xx_acx_set_preamble(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum acx_preamble_type preamble)
+{
+ struct acx_preamble *acx;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx_set_preamble");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->role_id = wlvif->role_id;
+ acx->preamble = preamble;
+
+ ret = cc33xx_cmd_configure(cc, PREAMBLE_TYPE_CFG, acx, sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("Setting of preamble failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int cc33xx_acx_cts_protect(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum acx_ctsprotect_type ctsprotect)
+{
+ struct acx_ctsprotect *acx;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx_set_ctsprotect");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->role_id = wlvif->role_id;
+ acx->ctsprotect = ctsprotect;
+
+ ret = cc33xx_cmd_configure(cc, CTS_PROTECTION_CFG, acx, sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("Setting of ctsprotect failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int cc33xx_acx_statistics(struct cc33xx *cc, void *stats)
+{
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx statistics");
+
+ ret = cc33xx_cmd_interrogate(cc, ACX_STATISTICS, stats,
+ sizeof(struct acx_header),
+ sizeof(struct cc33xx_acx_statistics));
+ if (ret < 0) {
+ cc33xx_warning("acx statistics failed: %d", ret);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int cc33xx_update_ap_rates(struct cc33xx *cc, u8 role_id,
+ u32 basic_rates_set, u32 supported_rates)
+{
+ struct ap_rates_class_cfg *cfg;
+ int ret;
+
+ cc33xx_debug(DEBUG_AP,
+ "Attempting to Update Basic Rates and Supported Rates");
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+
+ if (!cfg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cfg->basic_rates_set = cpu_to_le32(basic_rates_set);
+ cfg->supported_rates = cpu_to_le32(supported_rates);
+ cfg->role_id = role_id;
+ ret = cc33xx_cmd_configure(cc, AP_RATES_CFG, cfg, sizeof(*cfg));
+ if (ret < 0) {
+ cc33xx_warning("Updating AP Rates failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(cfg);
+ return ret;
+}
+
+int cc33xx_tx_param_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 ac,
+ u8 cw_min, u16 cw_max, u8 aifsn, u16 txop, bool acm,
+ u8 ps_scheme, u8 is_mu_edca, u8 mu_edca_aifs,
+ u8 mu_edca_ecw_min_max, u8 mu_edca_timer)
+{
+ struct tx_param_cfg *cfg;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_ACX,
+ "tx param cfg %d cw_ming %d cw_max %d aifs %d txop %d",
+ ac, cw_min, cw_max, aifsn, txop);
+
+ cc33xx_debug(DEBUG_ACX, "tx param cfg ps_scheme %d is_mu_edca %d mu_edca_aifs %d mu_edca_ecw_min_max %d mu_edca_timer %d",
+ ps_scheme, is_mu_edca, mu_edca_aifs, mu_edca_ecw_min_max,
+ mu_edca_timer);
+
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+
+ if (!cfg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cfg->role_id = wlvif->role_id;
+ cfg->ac = ac;
+ cfg->cw_min = cw_min;
+ cfg->cw_max = cpu_to_le16(cw_max);
+ cfg->aifsn = aifsn;
+ cfg->tx_op_limit = cpu_to_le16(txop);
+ cfg->acm = cpu_to_le16(acm);
+ cfg->ps_scheme = ps_scheme;
+ cfg->is_mu_edca = is_mu_edca;
+ cfg->mu_edca_aifs = mu_edca_aifs;
+ cfg->mu_edca_ecw_min_max = mu_edca_ecw_min_max;
+ cfg->mu_edca_timer = mu_edca_timer;
+
+ ret = cc33xx_cmd_configure(cc, TX_PARAMS_CFG, cfg, sizeof(*cfg));
+ if (ret < 0) {
+ cc33xx_warning("tx param cfg failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(cfg);
+ return ret;
+}
+
+int cc33xx_acx_init_mem_config(struct cc33xx *cc)
+{
+ int ret;
+
+ cc->target_mem_map = kzalloc(sizeof(*cc->target_mem_map),
+ GFP_KERNEL);
+ if (!cc->target_mem_map) {
+ cc33xx_error("couldn't allocate target memory map");
+ return -ENOMEM;
+ }
+
+ /* we now ask for the firmware built memory map */
+ ret = cc33xx_acx_mem_map(cc, (void *)cc->target_mem_map,
+ sizeof(struct cc33xx_acx_mem_map));
+ if (ret < 0) {
+ cc33xx_error("couldn't retrieve firmware memory map");
+ kfree(cc->target_mem_map);
+ cc->target_mem_map = NULL;
+ return ret;
+ }
+
+ /* initialize TX block book keeping */
+ cc->tx_blocks_available =
+ le32_to_cpu(cc->target_mem_map->num_tx_mem_blocks);
+ cc33xx_debug(DEBUG_TX, "available tx blocks: %d",
+ cc->tx_blocks_available);
+
+ cc33xx_debug(DEBUG_TX,
+ "available tx descriptor: %d available rx blocks %d",
+ cc->target_mem_map->num_tx_descriptor,
+ cc->target_mem_map->num_rx_mem_blocks);
+
+ return 0;
+}
+
+int cc33xx_acx_init_get_fw_versions(struct cc33xx *cc)
+{
+ int ret;
+
+ cc->all_versions.fw_ver = kzalloc(sizeof(*cc->all_versions.fw_ver),
+ GFP_KERNEL);
+ if (!cc->all_versions.fw_ver) {
+ cc33xx_error("couldn't allocate cc33xx_acx_fw_versions");
+ return -ENOMEM;
+ }
+
+ ret = cc33xx_acx_get_fw_versions(cc, (void *)cc->all_versions.fw_ver,
+ sizeof(struct cc33xx_acx_fw_versions));
+ if (ret < 0) {
+ cc33xx_error("couldn't retrieve firmware versions");
+ kfree(cc->all_versions.fw_ver);
+ cc->all_versions.fw_ver = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+int cc33xx_acx_set_ht_information(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u16 ht_operation_mode, u32 he_oper_params,
+ u16 he_oper_nss_set)
+{
+ struct cc33xx_acx_ht_information *acx;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_ACX, "acx ht information setting");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->role_id = wlvif->role_id;
+ acx->ht_protection =
+ (u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION);
+ acx->rifs_mode = 0;
+ acx->gf_protection =
+ !!(ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+
+ acx->dual_cts_protection = 0;
+
+ cc33xx_debug(DEBUG_ACX, "HE operation: 0x%xm mcs: 0x%x",
+ he_oper_params, he_oper_nss_set);
+
+ acx->he_operation = cpu_to_le32(he_oper_params);
+ acx->bss_basic_mcs_set = cpu_to_le16(he_oper_nss_set);
+ acx->qos_info_more_data_ack_bit = 0;
+ ret = cc33xx_cmd_configure(cc, BSS_OPERATION_CFG, acx, sizeof(*acx));
+
+ if (ret < 0) {
+ cc33xx_warning("acx ht information setting failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+/* setup BA session receiver setting in the FW. */
+int cc33xx_acx_set_ba_receiver_session(struct cc33xx *cc, u8 tid_index, u16 ssn,
+ bool enable, u8 peer_hlid, u8 win_size)
+{
+ struct cc33xx_acx_ba_receiver_setup *acx;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx ba receiver session setting");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->hlid = peer_hlid;
+ acx->tid = tid_index;
+ acx->enable = enable;
+ acx->win_size = win_size;
+ acx->ssn = cpu_to_le16(ssn);
+
+ ret = cc33xx_cmd_configure_failsafe(cc, BA_SESSION_RX_SETUP_CFG,
+ acx, sizeof(*acx),
+ BIT(CMD_STATUS_NO_RX_BA_SESSION));
+ if (ret < 0) {
+ cc33xx_warning("acx ba receiver session failed: %d", ret);
+ goto out;
+ }
+
+ /* sometimes we can't start the session */
+ if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
+ cc33xx_warning("no fw rx ba on tid %d", tid_index);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = 0;
+out:
+ kfree(acx);
+ return ret;
+}
+
+int cc33xx_acx_tsf_info(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u64 *mactime)
+{
+ struct cc33xx_acx_fw_tsf_information *tsf_info;
+ int ret = 0;
+
+ tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL);
+ if (!tsf_info) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ tsf_info->role_id = wlvif->role_id;
+
+ *mactime = le32_to_cpu(tsf_info->current_tsf_low) |
+ ((u64)le32_to_cpu(tsf_info->current_tsf_high) << 32);
+
+out:
+ kfree(tsf_info);
+ return ret;
+}
+
+int cc33xx_acx_config_ps(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct cc33xx_acx_config_ps *config_ps;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx config ps");
+
+ config_ps = kzalloc(sizeof(*config_ps), GFP_KERNEL);
+ if (!config_ps) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ config_ps->exit_retries = cc->conf.host_conf.conn.psm_exit_retries;
+ config_ps->enter_retries = cc->conf.host_conf.conn.psm_entry_retries;
+ config_ps->null_data_rate = cpu_to_le32(wlvif->basic_rate);
+
+ ret = cc33xx_cmd_configure(cc, ACX_CONFIG_PS, config_ps,
+ sizeof(*config_ps));
+
+ if (ret < 0) {
+ cc33xx_warning("acx config ps failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(config_ps);
+ return ret;
+}
+
+int cc33xx_acx_average_rssi(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, s8 *avg_rssi)
+{
+ struct acx_roaming_stats *acx;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_ACX, "acx roaming statistics");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_interrogate(cc, RSSI_INTR,
+ acx, sizeof(*acx), sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("acx roaming statistics failed: %d", ret);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ *avg_rssi = acx->rssi_beacon;
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+static const u16 cc33xx_idx_to_rate_100kbps[] = {
+ 10, 20, 55, 110, 60, 90, 120, 180, 240, 360, 480, 540
+};
+
+int cc33xx_acx_get_tx_rate(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct station_info *sinfo)
+{
+ struct acx_preamble_and_tx_rate *acx;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx set tx rate");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_interrogate(cc, GET_PREAMBLE_AND_TX_RATE_INTR,
+ acx, sizeof(*acx), sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("acx get preamble and tx rate failed: %d", ret);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ sinfo->txrate.flags = 0;
+ if (acx->preamble == CONF_PREAMBLE_TYPE_AC_VHT)
+ sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ else if ((acx->preamble >= CONF_PREAMBLE_TYPE_AX_SU) &&
+ (acx->preamble <= CONF_PREAMBLE_TYPE_AX_TB_NDP_FB))
+ sinfo->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ else if ((acx->preamble == CONF_PREAMBLE_TYPE_N_MIXED_MODE) ||
+ (acx->preamble == CONF_PREAMBLE_TYPE_GREENFIELD))
+ sinfo->txrate.flags = RATE_INFO_FLAGS_MCS;
+
+ if (acx->tx_rate >= CONF_HW_RATE_INDEX_MCS0)
+ sinfo->txrate.mcs = acx->tx_rate - CONF_HW_RATE_INDEX_MCS0;
+ else
+ sinfo->txrate.legacy = cc33xx_idx_to_rate_100kbps[acx->tx_rate - 1];
+
+ sinfo->txrate.nss = 1;
+ sinfo->txrate.bw = RATE_INFO_BW_20;
+ sinfo->txrate.he_gi = NL80211_RATE_INFO_HE_GI_3_2;
+ sinfo->txrate.he_dcm = 0;
+ sinfo->txrate.he_ru_alloc = 0;
+ sinfo->txrate.n_bonded_ch = 0;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/* Set the global behaviour of RX filters - On/Off + default action */
+int cc33xx_acx_default_rx_filter_enable(struct cc33xx *cc, bool enable,
+ enum rx_filter_action action)
+{
+ struct acx_default_rx_filter *acx;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx default rx filter en: %d act: %d",
+ enable, action);
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx)
+ return -ENOMEM;
+
+ acx->enable = enable;
+ acx->default_action = action;
+ acx->special_packet_bitmask = 0;
+
+ ret = cc33xx_cmd_configure(cc, ACX_ENABLE_RX_DATA_FILTER, acx,
+ sizeof(*acx));
+ if (ret < 0) {
+ cc33xx_warning("acx default rx filter enable failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+static int cc33xx_rx_filter_get_fields_size(struct cc33xx_rx_filter *filter)
+{
+ int i, fields_size = 0;
+
+ for (i = 0; i < filter->num_fields; i++) {
+ fields_size += filter->fields[i].len - sizeof(u8 *)
+ + sizeof(struct cc33xx_rx_filter_field);
+ }
+
+ return fields_size;
+}
+
+static void cc33xx_rx_filter_flatten_fields(struct cc33xx_rx_filter *filter,
+ u8 *buf)
+{
+ int i;
+ struct cc33xx_rx_filter_field *field;
+
+ for (i = 0; i < filter->num_fields; i++) {
+ field = (struct cc33xx_rx_filter_field *)buf;
+
+ field->offset = filter->fields[i].offset;
+ field->flags = filter->fields[i].flags;
+ field->len = filter->fields[i].len;
+
+ memcpy(&field->pattern, filter->fields[i].pattern, field->len);
+ buf += sizeof(struct cc33xx_rx_filter_field) - sizeof(u8 *);
+ buf += field->len;
+ }
+}
+
+/* Configure or disable a specific RX filter pattern */
+int cc33xx_acx_set_rx_filter(struct cc33xx *cc, u8 index, bool enable,
+ struct cc33xx_rx_filter *filter)
+{
+ struct acx_rx_filter_cfg *acx;
+ int fields_size = 0;
+ int acx_size;
+ int ret;
+
+ WARN_ON(enable && !filter);
+ WARN_ON(index >= CC33XX_MAX_RX_FILTERS);
+
+ cc33xx_debug(DEBUG_ACX,
+ "acx set rx filter idx: %d enable: %d filter: %p",
+ index, enable, filter);
+
+ if (enable) {
+ fields_size = cc33xx_rx_filter_get_fields_size(filter);
+
+ cc33xx_debug(DEBUG_ACX, "act: %d num_fields: %d field_size: %d",
+ filter->action, filter->num_fields, fields_size);
+ }
+
+ acx_size = ALIGN(sizeof(*acx) + fields_size, 4);
+ acx = kzalloc(acx_size, GFP_KERNEL);
+
+ if (!acx)
+ return -ENOMEM;
+
+ acx->enable = enable;
+ acx->index = index;
+
+ if (enable) {
+ acx->num_fields = filter->num_fields;
+ acx->action = filter->action;
+ cc33xx_rx_filter_flatten_fields(filter, acx->fields);
+ }
+
+ cc33xx_dump(DEBUG_ACX, "RX_FILTER: ", acx, acx_size);
+
+ ret = cc33xx_cmd_configure(cc, ACX_SET_RX_DATA_FILTER, acx, acx_size);
+ if (ret < 0) {
+ cc33xx_warning("setting rx filter failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+#endif /* CONFIG_PM */
+
+/* this command is basically the same as cc33xx_acx_ht_capabilities,
+ * with the addition of supported rates. they should be unified in
+ * the next fw api change
+ */
+int cc33xx_acx_set_peer_cap(struct cc33xx *cc,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_sta_he_cap *he_cap,
+ struct cc33xx_vif *wlvif, bool allow_ht_operation,
+ u32 rate_set, u8 hlid)
+{
+ struct cc33xx_acx_peer_cap *acx;
+ int ret = 0;
+ u32 ht_capabilites = 0;
+ u8 *cap_info = NULL;
+ u8 dcm_max_const_rx_mask = IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK;
+ u8 partial_bw_ext_range = IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE;
+
+ cc33xx_debug(DEBUG_ACX,
+ "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x",
+ ht_cap->ht_supported, ht_cap->cap, rate_set);
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (allow_ht_operation && ht_cap->ht_supported) {
+ /* no need to translate capabilities - use the spec values */
+ ht_capabilites = ht_cap->cap;
+
+ /* this bit is not employed by the spec but only by FW to
+ * indicate peer HT support
+ */
+ ht_capabilites |= CC33XX_HT_CAP_HT_OPERATION;
+
+ /* get data from A-MPDU parameters field */
+ acx->ampdu_max_length = ht_cap->ampdu_factor;
+ acx->ampdu_min_spacing = ht_cap->ampdu_density;
+ }
+
+ acx->ht_capabilites = cpu_to_le32(ht_capabilites);
+ acx->supported_rates = cpu_to_le32(rate_set);
+
+ acx->role_id = wlvif->role_id;
+ acx->has_he = he_cap->has_he;
+ memcpy(acx->mac_cap_info, he_cap->he_cap_elem.mac_cap_info, 6);
+ cap_info = he_cap->he_cap_elem.phy_cap_info;
+ acx->nominal_packet_padding = (cap_info[8] & NOMINAL_PACKET_PADDING);
+ /* Max DCM constelation for RX - bits [4:3] in PHY capabilities byte 3 */
+ acx->dcm_max_constelation = (cap_info[3] & dcm_max_const_rx_mask) >> 3;
+ acx->er_upper_supported = ((cap_info[6] & partial_bw_ext_range) != 0);
+ ret = cc33xx_cmd_configure(cc, PEER_CAP_CFG, acx, sizeof(*acx));
+
+ if (ret < 0) {
+ cc33xx_warning("acx ht capabilities setting failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int cc33xx_acx_trigger_fw_assert(struct cc33xx *cc)
+{
+ struct debug_header *buf;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "acx trigger firmware assert");
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = cc33xx_cmd_debug(cc, TRIGGER_FW_ASSERT, buf, sizeof(*buf));
+ if (ret < 0) {
+ cc33xx_error("failed to trigger firmware assert");
+ goto out;
+ }
+
+out:
+ kfree(buf);
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/cc33xx/acx.h b/drivers/net/wireless/ti/cc33xx/acx.h
new file mode 100644
index 000000000000..f250758c3da5
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/acx.h
@@ -0,0 +1,835 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __ACX_H__
+#define __ACX_H__
+
+#include "cmd.h"
+#include "debug.h"
+
+enum {
+ /* Regular PS: simple sending of packets */
+ PS_SCHEME_LEGACY = 0,
+ /* UPSD: sending a packet triggers a UPSD downstream*/
+ PS_SCHEME_UPSD_TRIGGER = 1,
+ /* Mixed mode is partially supported: we are not going to sleep, and
+ * triggers (on APSD AC's) are not sent when service period ends with
+ * more_data = 1.
+ */
+ PS_SCHEME_MIXED_MODE = 2,
+ /* Legacy PSPOLL: a PSPOLL packet will be sent before every data packet
+ * transmission in this queue.
+ */
+ PS_SCHEME_LEGACY_PSPOLL = 3,
+ /* Scheduled APSD mode. */
+ PS_SCHEME_SAPSD = 4,
+ /* No PSPOLL: move to active after first packet. no need to sent pspoll */
+ PS_SCHEME_NOPSPOLL = 5,
+
+ MAX_PS_SCHEME = PS_SCHEME_NOPSPOLL
+};
+
+/* Target's information element */
+struct acx_header {
+ struct cc33xx_cmd_header cmd;
+
+ /* acx (or information element) header */
+ __le16 id;
+
+ /* payload length (not including headers */
+ __le16 len;
+} __packed;
+
+struct debug_header {
+ struct cc33xx_cmd_header cmd;
+
+ /* debug (or information element) header */
+ __le16 id;
+
+ /* payload length (not including headers */
+ __le16 len;
+} __packed;
+
+enum cc33xx_role {
+ CC33XX_ROLE_STA = 0,
+ CC33XX_ROLE_IBSS,
+ CC33XX_ROLE_AP,
+ CC33XX_ROLE_DEVICE,
+ CC33XX_ROLE_P2P_CL,
+ CC33XX_ROLE_P2P_GO,
+ CC33XX_ROLE_MESH_POINT,
+
+ ROLE_TRANSCEIVER = 16,
+
+ CC33XX_INVALID_ROLE_TYPE = 0xff
+};
+
+enum cc33xx_psm_mode {
+ /* Active mode */
+ CC33XX_PSM_CAM = 0,
+
+ /* Power save mode */
+ CC33XX_PSM_PS = 1,
+
+ /* Extreme low power */
+ CC33XX_PSM_ELP = 2,
+
+ CC33XX_PSM_MAX = CC33XX_PSM_ELP,
+
+ /* illegal out of band value of PSM mode */
+ CC33XX_PSM_ILLEGAL = 0xff
+};
+
+struct acx_sleep_auth {
+ struct acx_header header;
+
+ /* The sleep level authorization of the device. */
+ /* 0 - Always active*/
+ /* 1 - Power down mode: light / fast sleep*/
+ /* 2 - ELP mode: Deep / Max sleep*/
+ u8 sleep_auth;
+ u8 padding[3];
+} __packed;
+
+enum acx_slot_type {
+ SLOT_TIME_LONG = 0,
+ SLOT_TIME_SHORT = 1,
+ DEFAULT_SLOT_TIME = SLOT_TIME_SHORT,
+ MAX_SLOT_TIMES = 0xFF
+};
+
+struct acx_slot {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 slot_time;
+ u8 reserved[2];
+} __packed;
+
+#define ACX_MC_ADDRESS_GROUP_MAX (20)
+#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX)
+
+struct acx_dot11_grp_addr_tbl {
+ struct acx_header header;
+
+ u8 enabled;
+ u8 num_groups;
+ u8 pad[2];
+ u8 mac_table[ADDRESS_GROUP_MAX_LEN];
+} __packed;
+
+struct acx_beacon_filter_option {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 enable;
+ /* The number of beacons without the unicast TIM
+ * bit set that the firmware buffers before
+ * signaling the host about ready frames.
+ * When set to 0 and the filter is enabled, beacons
+ * without the unicast TIM bit set are dropped.
+ */
+ u8 max_num_beacons;
+ u8 pad;
+} __packed;
+
+/* ACXBeaconFilterEntry (not 221)
+ * Byte Offset Size (Bytes) Definition
+ * =========== ============ ==========
+ * 0 1 IE identifier
+ * 1 1 Treatment bit mask
+ *
+ * ACXBeaconFilterEntry (221)
+ * Byte Offset Size (Bytes) Definition
+ * =========== ============ ==========
+ * 0 1 IE identifier
+ * 1 1 Treatment bit mask
+ * 2 3 OUI
+ * 5 1 Type
+ * 6 2 Version
+ *
+ *
+ * Treatment bit mask - The information element handling:
+ * bit 0 - The information element is compared and transferred
+ * in case of change.
+ * bit 1 - The information element is transferred to the host
+ * with each appearance or disappearance.
+ * Note that both bits can be set at the same time.
+ */
+
+enum {
+ BEACON_FILTER_TABLE_MAX_IE_NUM = 32,
+ BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM = 6,
+ BEACON_FILTER_TABLE_IE_ENTRY_SIZE = 2,
+ BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE = 6
+};
+
+#define BEACON_FILTER_TABLE_MAX_SIZE \
+ ((BEACON_FILTER_TABLE_MAX_IE_NUM * \
+ BEACON_FILTER_TABLE_IE_ENTRY_SIZE) + \
+ (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \
+ BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE))
+
+struct acx_beacon_filter_ie_table {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 num_ie;
+ u8 pad[2];
+ u8 table[BEACON_FILTER_TABLE_MAX_SIZE];
+} __packed;
+
+struct acx_energy_detection {
+ struct acx_header header;
+
+ /* The RX Clear Channel Assessment threshold in the PHY */
+ __le16 rx_cca_threshold;
+ u8 tx_energy_detection;
+ u8 pad;
+} __packed;
+
+struct acx_event_mask {
+ struct acx_header header;
+
+ __le32 event_mask;
+ __le32 high_event_mask; /* Unused */
+} __packed;
+
+struct acx_tx_power_cfg {
+ struct acx_header header;
+
+ u8 role_id;
+ s8 tx_power;
+ u8 padding[2];
+} __packed;
+
+struct acx_wake_up_condition {
+ struct acx_header header;
+
+ u8 wake_up_event;
+ u8 listen_interval;
+ u8 padding[2];
+} __packed;
+
+struct assoc_info_cfg {
+ struct acx_header header;
+
+ u8 role_id;
+ __le16 aid;
+ u8 wmm_enabled;
+ u8 nontransmitted;
+ u8 bssid_index;
+ u8 bssid_indicator;
+ u8 transmitter_bssid[ETH_ALEN];
+ u8 ht_supported;
+ u8 vht_supported;
+ u8 has_he;
+} __packed;
+
+enum acx_preamble_type {
+ ACX_PREAMBLE_LONG = 0,
+ ACX_PREAMBLE_SHORT = 1
+};
+
+struct acx_preamble {
+ struct acx_header header;
+
+ /* When set, the WiLink transmits the frames with a short preamble and
+ * when cleared, the WiLink transmits the frames with a long preamble.
+ */
+ u8 role_id;
+ u8 preamble;
+ u8 padding[2];
+} __packed;
+
+enum acx_ctsprotect_type {
+ CTSPROTECT_DISABLE = 0,
+ CTSPROTECT_ENABLE = 1
+};
+
+struct acx_ctsprotect {
+ struct acx_header header;
+ u8 role_id;
+ u8 ctsprotect;
+ u8 padding[2];
+} __packed;
+
+struct ap_rates_class_cfg {
+ struct acx_header header;
+ u8 role_id;
+ __le32 basic_rates_set;
+ __le32 supported_rates;
+ u8 padding[3];
+} __packed;
+
+struct tx_param_cfg {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 ac;
+ u8 aifsn;
+ u8 cw_min;
+
+ __le16 cw_max;
+ __le16 tx_op_limit;
+
+ __le16 acm;
+
+ u8 ps_scheme;
+
+ u8 is_mu_edca;
+ u8 mu_edca_aifs;
+ u8 mu_edca_ecw_min_max;
+ u8 mu_edca_timer;
+
+ u8 reserved;
+} __packed;
+
+struct cc33xx_acx_config_memory {
+ struct acx_header header;
+
+ u8 rx_mem_block_num;
+ u8 tx_min_mem_block_num;
+ u8 num_stations;
+ u8 num_ssid_profiles;
+ __le32 total_tx_descriptors;
+ u8 dyn_mem_enable;
+ u8 tx_free_req;
+ u8 rx_free_req;
+ u8 tx_min;
+ u8 fwlog_blocks;
+ u8 padding[3];
+} __packed;
+
+struct cc33xx_acx_mem_map {
+ struct acx_header header;
+
+ /* Number of blocks FW allocated for TX packets */
+ __le32 num_tx_mem_blocks;
+
+ /* Number of blocks FW allocated for RX packets */
+ __le32 num_rx_mem_blocks;
+
+ /* Number of TX descriptor that allocated. */
+ __le32 num_tx_descriptor;
+
+ __le32 tx_result;
+
+} __packed;
+
+struct cc33xx_acx_fw_versions {
+ struct acx_header header;
+
+ __le16 major_version;
+ __le16 minor_version;
+ __le16 api_version;
+ __le16 build_version;
+
+ u8 phy_version[6];
+ u8 padding[2];
+} __packed;
+
+/* special capability bit (not employed by the 802.11n spec) */
+#define CC33XX_HT_CAP_HT_OPERATION BIT(16)
+
+/* ACX_HT_BSS_OPERATION
+ * Configure HT capabilities - AP rules for behavior in the BSS.
+ */
+struct cc33xx_acx_ht_information {
+ struct acx_header header;
+
+ u8 role_id;
+
+ /* Values: 0 - RIFS not allowed, 1 - RIFS allowed */
+ u8 rifs_mode;
+
+ /* Values: 0 - 3 like in spec */
+ u8 ht_protection;
+
+ /* Values: 0 - GF protection not required, 1 - GF protection required */
+ u8 gf_protection;
+
+ /* Values: 0 - Dual CTS protection not required,
+ * 1 - Dual CTS Protection required
+ * Note: When this value is set to 1 FW will protect all TXOP with RTS
+ * frame and will not use CTS-to-self regardless of the value of the
+ * ACX_CTS_PROTECTION information element
+ */
+ u8 dual_cts_protection;
+
+ __le32 he_operation;
+
+ __le16 bss_basic_mcs_set;
+ u8 qos_info_more_data_ack_bit;
+
+} __packed;
+
+struct cc33xx_acx_ba_receiver_setup {
+ struct acx_header header;
+
+ /* Specifies link id, range 0-31 */
+ u8 hlid;
+
+ u8 tid;
+
+ u8 enable;
+
+ /* Windows size in number of packets */
+ u8 win_size;
+
+ /* BA session starting sequence number. RANGE 0-FFF */
+ __le16 ssn;
+
+ u8 padding[2];
+} __packed;
+
+struct cc33xx_acx_fw_tsf_information {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 padding1[3];
+ __le32 current_tsf_high;
+ __le32 current_tsf_low;
+ __le32 last_bttt_high;
+ __le32 last_tbtt_low;
+ u8 last_dtim_count;
+ u8 padding2[3];
+} __packed;
+
+struct cc33xx_acx_config_ps {
+ struct acx_header header;
+
+ u8 exit_retries;
+ u8 enter_retries;
+ u8 padding[2];
+ __le32 null_data_rate;
+} __packed;
+
+#define ACX_RATE_MGMT_ALL_PARAMS 0xff
+
+struct acx_default_rx_filter {
+ struct acx_header header;
+ u8 enable;
+
+ /* action of type FILTER_XXX */
+ u8 default_action;
+
+ /* special packet bitmask - packet that use for trigger the host */
+ u8 special_packet_bitmask;
+
+ u8 padding;
+} __packed;
+
+struct acx_rx_filter_cfg {
+ struct acx_header header;
+
+ u8 enable;
+
+ /* 0 - WL1271_MAX_RX_FILTERS-1 */
+ u8 index;
+
+ u8 action;
+
+ u8 num_fields;
+ u8 fields[];
+} __packed;
+
+struct acx_roaming_stats {
+ struct acx_header header;
+
+ u8 role_id;
+ u8 pad[3];
+ __le32 missed_beacons;
+ u8 snr_data;
+ u8 snr_bacon;
+ s8 rssi_data;
+ s8 rssi_beacon;
+} __packed;
+
+enum cfg {
+ CTS_PROTECTION_CFG = 0,
+ TX_PARAMS_CFG = 1,
+ ASSOC_INFO_CFG = 2,
+ PEER_CAP_CFG = 3,
+ BSS_OPERATION_CFG = 4,
+ SLOT_CFG = 5,
+ PREAMBLE_TYPE_CFG = 6,
+ DOT11_GROUP_ADDRESS_TBL = 7,
+ BA_SESSION_RX_SETUP_CFG = 8,
+ ACX_SLEEP_AUTH = 9,
+ STATIC_CALIBRATION_CFG = 10,
+ AP_RATES_CFG = 11,
+ WAKE_UP_CONDITIONS_CFG = 12,
+ SET_ANTENNA_SELECT_CFG = 13,
+ TX_POWER_CFG = 14,
+ VENDOR_IE_CFG = 15,
+ START_COEX_STATISTICS_CFG = 16,
+ BEACON_FILTER_OPT = 17,
+ BEACON_FILTER_TABLE = 18,
+ ACX_ENABLE_RX_DATA_FILTER = 19,
+ ACX_SET_RX_DATA_FILTER = 20,
+ ACX_GET_DATA_FILTER_STATISTICS = 21,
+ TWT_SETUP = 22,
+ TWT_TERMINATE = 23,
+ TWT_SUSPEND = 24,
+ TWT_RESUME = 25,
+ ANT_DIV_ENABLE = 26,
+ ANT_DIV_SET_RSSI_THRESHOLD = 27,
+ ANT_DIV_SELECT_DEFAULT_ANTENNA = 28,
+
+ LAST_CFG_VALUE,
+ MAX_DOT11_CFG = LAST_CFG_VALUE,
+
+ MAX_CFG = 0xFFFF /*force enumeration to 16bits*/
+};
+
+enum cmd_debug {
+ UPLINK_MULTI_USER_CFG,
+ UPLINK_MULTI_USER_DATA_CFG,
+ OPERATION_MODE_CTRL_CFG,
+ UPLINK_POWER_HEADER_CFG,
+ MCS_FIXED_RATE_CFG,
+ GI_LTF_CFG,
+ TRANSMIT_OMI_CFG,
+ TB_ONLY_CFG,
+ BA_SESSION_CFG,
+ FORCE_PS_CFG,
+ RATE_OVERRRIDE_CFG,
+ BLS_CFG,
+ BLE_ENABLE,
+ SET_TSF,
+ RTS_TH_CFG,
+ LINK_ADAPT_CFG,
+ CALIB_BITMAP_CFG,
+ PWR_PARTIAL_MODES_CFG,
+ TRIGGER_FW_ASSERT,
+ BURST_MODE_CFG,
+
+ LAST_DEBUG_VALUE,
+
+ MAX_DEBUG = 0xFFFF /*force enumeration to 16bits*/
+
+};
+
+enum interrogate_opt {
+ MEM_MAP_INTR = 0,
+ GET_FW_VERSIONS_INTR = 1,
+ RSSI_INTR = 2,
+ GET_ANTENNA_SELECT_INTR = 3,
+ GET_PREAMBLE_AND_TX_RATE_INTR = 4,
+ GET_MAC_ADDRESS = 5,
+ READ_COEX_STATISTICS = 6,
+ LAST_IE_VALUE,
+ MAX_DOT11_IE = LAST_IE_VALUE,
+
+ MAX_IE = 0xFFFF /*force enumeration to 16bits*/
+};
+
+enum {
+ ACX_STATISTICS = LAST_CFG_VALUE,
+ ACX_CONFIG_PS,
+ ACX_CLEAR_STATISTICS = 0x0054,
+};
+
+struct cc33xx_acx_error_stats {
+ __le32 error_frame_non_ctrl;
+ __le32 error_frame_ctrl;
+ __le32 error_frame_during_protection;
+ __le32 null_frame_tx_start;
+ __le32 null_frame_cts_start;
+ __le32 bar_retry;
+ __le32 num_frame_cts_nul_flid;
+ __le32 tx_abort_failure;
+ __le32 tx_resume_failure;
+ __le32 rx_cmplt_db_overflow_cnt;
+ __le32 elp_while_rx_exch;
+ __le32 elp_while_tx_exch;
+ __le32 elp_while_tx;
+ __le32 elp_while_nvic_pending;
+ __le32 rx_excessive_frame_len;
+ __le32 burst_mismatch;
+ __le32 tbc_exch_mismatch;
+} __packed;
+
+#define NUM_OF_RATES_INDEXES 30
+struct cc33xx_acx_tx_stats {
+ __le32 tx_prepared_descs;
+ __le32 tx_cmplt;
+ __le32 tx_template_prepared;
+ __le32 tx_data_prepared;
+ __le32 tx_template_programmed;
+ __le32 tx_data_programmed;
+ __le32 tx_burst_programmed;
+ __le32 tx_starts;
+ __le32 tx_stop;
+ __le32 tx_start_templates;
+ __le32 tx_start_int_templates;
+ __le32 tx_start_fw_gen;
+ __le32 tx_start_data;
+ __le32 tx_start_null_frame;
+ __le32 tx_exch;
+ __le32 tx_retry_template;
+ __le32 tx_retry_data;
+ __le32 tx_retry_per_rate[NUM_OF_RATES_INDEXES];
+ __le32 tx_exch_pending;
+ __le32 tx_exch_expiry;
+ __le32 tx_done_template;
+ __le32 tx_done_data;
+ __le32 tx_done_int_template;
+ __le32 tx_cfe1;
+ __le32 tx_cfe2;
+ __le32 frag_called;
+ __le32 frag_mpdu_alloc_failed;
+ __le32 frag_init_called;
+ __le32 frag_in_process_called;
+ __le32 frag_tkip_called;
+ __le32 frag_key_not_found;
+ __le32 frag_need_fragmentation;
+ __le32 frag_bad_mblk_num;
+ __le32 frag_failed;
+ __le32 frag_cache_hit;
+ __le32 frag_cache_miss;
+} __packed;
+
+struct cc33xx_acx_rx_stats {
+ __le32 rx_beacon_early_term;
+ __le32 rx_out_of_mpdu_nodes;
+ __le32 rx_hdr_overflow;
+ __le32 rx_dropped_frame;
+ __le32 rx_done_stage;
+ __le32 rx_done;
+ __le32 rx_defrag;
+ __le32 rx_defrag_end;
+ __le32 rx_cmplt;
+ __le32 rx_pre_complt;
+ __le32 rx_cmplt_task;
+ __le32 rx_phy_hdr;
+ __le32 rx_timeout;
+ __le32 rx_rts_timeout;
+ __le32 rx_timeout_wa;
+ __le32 defrag_called;
+ __le32 defrag_init_called;
+ __le32 defrag_in_process_called;
+ __le32 defrag_tkip_called;
+ __le32 defrag_need_defrag;
+ __le32 defrag_decrypt_failed;
+ __le32 decrypt_key_not_found;
+ __le32 defrag_need_decrypt;
+ __le32 rx_tkip_replays;
+ __le32 rx_xfr;
+} __packed;
+
+struct cc33xx_acx_isr_stats {
+ __le32 irqs;
+} __packed;
+
+#define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10
+
+struct cc33xx_acx_pwr_stats {
+ __le32 missing_bcns_cnt;
+ __le32 rcvd_bcns_cnt;
+ __le32 connection_out_of_sync;
+ __le32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD];
+ __le32 rcvd_awake_bcns_cnt;
+ __le32 sleep_time_count;
+ __le32 sleep_time_avg;
+ __le32 sleep_cycle_avg;
+ __le32 sleep_percent;
+ __le32 ap_sleep_active_conf;
+ __le32 ap_sleep_user_conf;
+ __le32 ap_sleep_counter;
+} __packed;
+
+struct cc33xx_acx_rx_filter_stats {
+ __le32 beacon_filter;
+ __le32 arp_filter;
+ __le32 mc_filter;
+ __le32 dup_filter;
+ __le32 data_filter;
+ __le32 ibss_filter;
+ __le32 protection_filter;
+ __le32 accum_arp_pend_requests;
+ __le32 max_arp_queue_dep;
+} __packed;
+
+struct cc33xx_acx_rx_rate_stats {
+ __le32 rx_frames_per_rates[50];
+} __packed;
+
+#define AGGR_STATS_TX_AGG 16
+#define AGGR_STATS_RX_SIZE_LEN 16
+
+struct cc33xx_acx_aggr_stats {
+ __le32 tx_agg_rate[AGGR_STATS_TX_AGG];
+ __le32 tx_agg_len[AGGR_STATS_TX_AGG];
+ __le32 rx_size[AGGR_STATS_RX_SIZE_LEN];
+} __packed;
+
+#define PIPE_STATS_HW_FIFO 11
+
+struct cc33xx_acx_pipeline_stats {
+ __le32 hs_tx_stat_fifo_int;
+ __le32 hs_rx_stat_fifo_int;
+ __le32 enc_tx_stat_fifo_int;
+ __le32 enc_rx_stat_fifo_int;
+ __le32 rx_complete_stat_fifo_int;
+ __le32 pre_proc_swi;
+ __le32 post_proc_swi;
+ __le32 sec_frag_swi;
+ __le32 pre_to_defrag_swi;
+ __le32 defrag_to_rx_xfer_swi;
+ __le32 dec_packet_in;
+ __le32 dec_packet_in_fifo_full;
+ __le32 dec_packet_out;
+ __le16 pipeline_fifo_full[PIPE_STATS_HW_FIFO];
+ __le16 padding;
+} __packed;
+
+#define DIVERSITY_STATS_NUM_OF_ANT 2
+
+struct cc33xx_acx_diversity_stats {
+ __le32 num_of_packets_per_ant[DIVERSITY_STATS_NUM_OF_ANT];
+ __le32 total_num_of_toggles;
+} __packed;
+
+struct cc33xx_acx_thermal_stats {
+ __le16 irq_thr_low;
+ __le16 irq_thr_high;
+ __le16 tx_stop;
+ __le16 tx_resume;
+ __le16 false_irq;
+ __le16 adc_source_unexpected;
+} __packed;
+
+#define CC33XX_NUM_OF_CALIBRATIONS_ERRORS 18
+struct cc33xx_acx_calib_failure_stats {
+ __le16 fail_count[CC33XX_NUM_OF_CALIBRATIONS_ERRORS];
+ __le32 calib_count;
+} __packed;
+
+struct cc33xx_roaming_stats {
+ s32 rssi_level;
+} __packed;
+
+struct cc33xx_dfs_stats {
+ __le32 num_of_radar_detections;
+} __packed;
+
+struct cc33xx_acx_statistics {
+ struct acx_header header;
+
+ struct cc33xx_acx_error_stats error;
+ struct cc33xx_acx_tx_stats tx;
+ struct cc33xx_acx_rx_stats rx;
+ struct cc33xx_acx_isr_stats isr;
+ struct cc33xx_acx_pwr_stats pwr;
+ struct cc33xx_acx_rx_filter_stats rx_filter;
+ struct cc33xx_acx_rx_rate_stats rx_rate;
+ struct cc33xx_acx_aggr_stats aggr_size;
+ struct cc33xx_acx_pipeline_stats pipeline;
+ struct cc33xx_acx_diversity_stats diversity;
+ struct cc33xx_acx_thermal_stats thermal;
+ struct cc33xx_acx_calib_failure_stats calib;
+ struct cc33xx_roaming_stats roaming;
+ struct cc33xx_dfs_stats dfs;
+} __packed;
+
+/* ACX_PEER_CAP
+ * this struct is very similar to cc33xx_acx_ht_capabilities, with the
+ * addition of supported rates
+ */
+#define NOMINAL_PACKET_PADDING (0xC0)
+struct cc33xx_acx_peer_cap {
+ struct acx_header header;
+
+ u8 role_id;
+
+ /* rates supported by the remote peer */
+ __le32 supported_rates;
+
+ /* bitmask of capability bits supported by the peer */
+ __le32 ht_capabilites;
+ /* This the maximum A-MPDU length supported by the AP. The FW may not
+ * exceed this length when sending A-MPDUs
+ */
+ u8 ampdu_max_length;
+
+ /* This is the minimal spacing required when sending A-MPDUs to the AP*/
+ u8 ampdu_min_spacing;
+
+ /* HE capabilities */
+ u8 mac_cap_info[8];
+
+ /* Nominal packet padding value, used for determining the packet extension duration */
+ u8 nominal_packet_padding;
+
+ /* HE peer support */
+ bool has_he;
+
+ u8 dcm_max_constelation;
+
+ u8 er_upper_supported;
+
+ u8 padding;
+} __packed;
+
+struct acx_preamble_and_tx_rate {
+ struct acx_header header;
+ u16 tx_rate;
+ u8 preamble;
+ u8 role_id;
+} __packed;
+
+int cc33xx_acx_wake_up_conditions(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 wake_up_event, u8 listen_interval);
+int cc33xx_acx_sleep_auth(struct cc33xx *cc, u8 sleep_auth);
+int cc33xx_ble_enable(struct cc33xx *cc, u8 ble_enable);
+int cc33xx_acx_tx_power(struct cc33xx *cc, struct cc33xx_vif *wlvif, int power);
+int cc33xx_acx_slot(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum acx_slot_type slot_time);
+int cc33xx_acx_group_address_tbl(struct cc33xx *cc, bool enable, void *mc_list, u32 mc_list_len);
+int cc33xx_acx_beacon_filter_opt(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ bool enable_filter);
+int cc33xx_acx_beacon_filter_table(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_assoc_info_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct ieee80211_sta *sta, u16 aid);
+int cc33xx_acx_set_preamble(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum acx_preamble_type preamble);
+int cc33xx_acx_cts_protect(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum acx_ctsprotect_type ctsprotect);
+int cc33xx_acx_statistics(struct cc33xx *cc, void *stats);
+int cc33xx_tx_param_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 ac,
+ u8 cw_min, u16 cw_max, u8 aifsn, u16 txop, bool acm,
+ u8 ps_scheme, u8 is_mu_edca, u8 mu_edca_aifs,
+ u8 mu_edca_ecw_min_max, u8 mu_edca_timer);
+int cc33xx_update_ap_rates(struct cc33xx *cc, u8 role_id,
+ u32 basic_rates_set, u32 supported_rates);
+int cc33xx_acx_init_mem_config(struct cc33xx *cc);
+int cc33xx_acx_init_get_fw_versions(struct cc33xx *cc);
+int cc33xx_acx_set_ht_information(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u16 ht_operation_mode, u32 he_oper_params,
+ u16 he_oper_nss_set);
+int cc33xx_acx_set_ba_receiver_session(struct cc33xx *cc, u8 tid_index, u16 ssn,
+ bool enable, u8 peer_hlid, u8 win_size);
+int cc33xx_acx_tsf_info(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u64 *mactime);
+int cc33xx_acx_config_ps(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_acx_get_tx_rate(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct station_info *sinfo);
+int cc33xx_acx_average_rssi(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, s8 *avg_rssi);
+int cc33xx_acx_default_rx_filter_enable(struct cc33xx *cc, bool enable,
+ enum rx_filter_action action);
+int cc33xx_acx_set_rx_filter(struct cc33xx *cc, u8 index, bool enable,
+ struct cc33xx_rx_filter *filter);
+int cc33xx_acx_clear_statistics(struct cc33xx *cc);
+int cc33xx_acx_set_peer_cap(struct cc33xx *cc,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_sta_he_cap *he_cap,
+ struct cc33xx_vif *wlvif, bool allow_ht_operation,
+ u32 rate_set, u8 hlid);
+int cc33xx_acx_trigger_fw_assert(struct cc33xx *cc);
+
+#endif /* __CC33XX_ACX_H__ */
--
2.25.1


2024-05-21 17:21:07

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 01/17] Add cc33xx.h, cc33xx_i.h

From: Michael Nemanov <[email protected]>

These are header files with definitions common to the entire driver.

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/cc33xx.h | 481 ++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/cc33xx_i.h | 459 +++++++++++++++++++++
2 files changed, 940 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx.h
create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx_i.h

diff --git a/drivers/net/wireless/ti/cc33xx/cc33xx.h b/drivers/net/wireless/ti/cc33xx/cc33xx.h
new file mode 100644
index 000000000000..3a2e56af4da7
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/cc33xx.h
@@ -0,0 +1,481 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __CC33XX_H__
+#define __CC33XX_H__
+
+#include "cc33xx_i.h"
+#include "rx.h"
+
+/* Wireless Driver Version */
+#define DRV_VERSION "1.7.0.108"
+
+/* The maximum number of Tx descriptors in all chip families */
+#define CC33XX_MAX_TX_DESCRIPTORS 32
+
+#define CC33XX_CMD_MAX_SIZE (896)
+#define CC33XX_INI_PARAM_COMMAND_SIZE (16UL)
+#define CC33XX_INI_CMD_MAX_SIZE (CC33X_CONF_SIZE + CC33XX_INI_PARAM_COMMAND_SIZE + sizeof(int))
+
+#define CC33XX_CMD_BUFFER_SIZE ((CC33XX_INI_CMD_MAX_SIZE > CC33XX_CMD_MAX_SIZE)\
+ ? CC33XX_INI_CMD_MAX_SIZE : CC33XX_CMD_MAX_SIZE)
+
+#define CC33XX_NUM_MAC_ADDRESSES 3
+
+#define CC33XX_AGGR_BUFFER_SIZE (8 * PAGE_SIZE)
+
+#define CC33XX_NUM_TX_DESCRIPTORS 32
+#define CC33XX_NUM_RX_DESCRIPTORS 32
+
+#define CC33XX_RX_BA_MAX_SESSIONS 13
+
+#define CC33XX_MAX_AP_STATIONS 16
+
+struct cc33xx_tx_hw_descr;
+struct cc33xx_rx_descriptor;
+struct partial_rx_frame;
+struct core_fw_status;
+struct core_status;
+
+enum wl_rx_buf_align;
+
+struct driver_fw_versions {
+ const char *driver_ver;
+ struct cc33xx_acx_fw_versions *fw_ver;
+};
+
+struct cc33xx_stats {
+ void *fw_stats;
+ unsigned long fw_stats_update;
+ unsigned int retry_count;
+ unsigned int excessive_retries;
+};
+
+struct cc33xx_ant_diversity {
+ u8 diversity_enable;
+ s8 rssi_threshold;
+ u8 default_antenna;
+ u8 padding;
+};
+
+struct cc33xx {
+ bool initialized;
+ struct ieee80211_hw *hw;
+ bool mac80211_registered;
+
+ struct device *dev;
+ struct platform_device *pdev;
+
+ struct cc33xx_if_operations *if_ops;
+
+ int wakeirq;
+
+ spinlock_t cc_lock; /* Protects critical sections */
+
+ enum cc33xx_state state;
+ bool plt;
+ enum plt_mode plt_mode;
+ u8 plt_role_id;
+ u8 fem_manuf;
+ u8 last_vif_count;
+ struct mutex mutex; /* Protect all CC33xx operations */
+ struct core_status *core_status;
+ u8 last_fw_rls_idx;
+ u8 command_result[CC33XX_CMD_MAX_SIZE];
+ u16 result_length;
+ struct partial_rx_frame partial_rx;
+
+ unsigned long flags;
+
+ void *nvs_mac_addr;
+ size_t nvs_mac_addr_len;
+ struct cc33xx_fw_download *fw_download;
+
+ struct mac_address addresses[CC33XX_NUM_MAC_ADDRESSES];
+
+ unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)];
+ unsigned long roles_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)];
+ unsigned long roc_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)];
+ unsigned long rate_policies_map[BITS_TO_LONGS(CC33XX_MAX_RATE_POLICIES)];
+
+ u8 session_ids[CC33XX_MAX_LINKS];
+
+ struct list_head wlvif_list;
+
+ u8 sta_count;
+ u8 ap_count;
+
+ struct cc33xx_acx_mem_map *target_mem_map;
+
+ /* Accounting for allocated / available TX blocks on HW */
+
+ u32 tx_blocks_available;
+ u32 tx_allocated_blocks;
+
+ /* Accounting for allocated / available Tx packets in HW */
+
+ u32 tx_allocated_pkts[NUM_TX_QUEUES];
+
+ /* Time-offset between host and chipset clocks */
+
+ /* Frames scheduled for transmission, not handled yet */
+ int tx_queue_count[NUM_TX_QUEUES];
+ unsigned long queue_stop_reasons[NUM_TX_QUEUES * CC33XX_NUM_MAC_ADDRESSES];
+
+ /* Frames received, not handled yet by mac80211 */
+ struct sk_buff_head deferred_rx_queue;
+
+ /* Frames sent, not returned yet to mac80211 */
+ struct sk_buff_head deferred_tx_queue;
+
+ struct work_struct tx_work;
+ struct workqueue_struct *freezable_wq;
+
+ /*freezable wq for netstack_work*/
+ struct workqueue_struct *freezable_netstack_wq;
+
+ /* Pending TX frames */
+ unsigned long tx_frames_map[BITS_TO_LONGS(CC33XX_MAX_TX_DESCRIPTORS)];
+ struct sk_buff *tx_frames[CC33XX_MAX_TX_DESCRIPTORS];
+ int tx_frames_cnt;
+
+ /* FW Rx counter */
+ u32 rx_counter;
+
+ /* Intermediate buffer, used for packet aggregation */
+ u8 *aggr_buf;
+ u32 aggr_buf_size;
+ size_t max_transaction_len;
+
+ /* Reusable dummy packet template */
+ struct sk_buff *dummy_packet;
+
+ /* Network stack work */
+ struct work_struct netstack_work;
+ /* FW log buffer */
+ u8 *fwlog;
+
+ /* Number of valid bytes in the FW log buffer */
+ ssize_t fwlog_size;
+
+ /* Hardware recovery work */
+ struct work_struct recovery_work;
+
+ struct work_struct irq_deferred_work;
+
+ /* Reg domain last configuration */
+ DECLARE_BITMAP(reg_ch_conf_last, 64);
+ /* Reg domain pending configuration */
+ DECLARE_BITMAP(reg_ch_conf_pending, 64);
+
+ /* Lock-less list for deferred event handling */
+ struct llist_head event_list;
+ /* The mbox event mask */
+ u32 event_mask;
+ /* events to unmask only when ap interface is up */
+ u32 ap_event_mask;
+
+ /* Are we currently scanning */
+ struct cc33xx_vif *scan_wlvif;
+ struct cc33xx_scan scan;
+ struct delayed_work scan_complete_work;
+
+ struct ieee80211_vif *roc_vif;
+ struct delayed_work roc_complete_work;
+
+ struct cc33xx_vif *sched_vif;
+
+ u8 mac80211_scan_stopped;
+
+ /* The current band */
+ enum nl80211_band band;
+
+ /* in dBm */
+ int power_level;
+
+ struct cc33xx_stats stats;
+
+ __le32 *buffer_32;
+
+ /* Current chipset configuration */
+ struct cc33xx_conf_file conf;
+
+ bool enable_11a;
+
+ /* bands supported by this instance of cc33xx */
+ struct ieee80211_supported_band bands[CC33XX_NUM_BANDS];
+
+ /* wowlan trigger was configured during suspend.
+ * (currently, only "ANY" and "PATTERN" trigger is supported)
+ */
+
+ bool keep_device_power;
+
+ /* AP-mode - links indexed by HLID. The global and broadcast links
+ * are always active.
+ */
+ struct cc33xx_link links[CC33XX_MAX_LINKS];
+
+ /* number of currently active links */
+ int active_link_count;
+
+ /* AP-mode - a bitmap of links currently in PS mode according to FW */
+ unsigned long ap_fw_ps_map;
+
+ /* AP-mode - a bitmap of links currently in PS mode in mac80211 */
+ unsigned long ap_ps_map;
+
+ /* Quirks of specific hardware revisions */
+ unsigned int quirks;
+
+ /* number of currently active RX BA sessions */
+ int ba_rx_session_count;
+
+ /* AP-mode - number of currently connected stations */
+ int active_sta_count;
+
+ /* last wlvif we transmitted from */
+ struct cc33xx_vif *last_wlvif;
+
+ /* work to fire when Tx is stuck */
+ struct delayed_work tx_watchdog_work;
+
+ /* HW HT (11n) capabilities */
+ struct ieee80211_sta_ht_cap ht_cap[CC33XX_NUM_BANDS];
+
+ /* the current dfs region */
+ enum nl80211_dfs_regions dfs_region;
+ bool radar_debug_mode;
+
+ /* RX Data filter rule state - enabled/disabled */
+ /* used in CONFIG PM AND W8 Code */
+ unsigned long rx_filter_enabled[BITS_TO_LONGS(CC33XX_MAX_RX_FILTERS)];
+
+ /* mutex for protecting the tx_flush function */
+ struct mutex flush_mutex;
+
+ /* sleep auth value currently configured to FW */
+ int sleep_auth;
+
+ /*ble_enable value - 1=enabled, 0=disabled. */
+ int ble_enable;
+
+ /* parameters for joining a TWT agreement */
+ int min_wake_duration_usec;
+ int min_wake_interval_mantissa;
+ int min_wake_interval_exponent;
+ int max_wake_interval_mantissa;
+ int max_wake_interval_exponent;
+
+ /* the number of allocated MAC addresses in this chip */
+ int num_mac_addr;
+
+ /* sta role index - if 0 - wlan0 primary station interface,
+ * if 1 - wlan2 - secondary station interface
+ */
+ u8 sta_role_idx;
+
+ u16 max_cmd_size;
+
+ struct completion nvs_loading_complete;
+ struct completion command_complete;
+
+ /* dynamic fw traces */
+ u32 dynamic_fw_traces;
+
+ /* buffer for sending commands to FW */
+ u8 cmd_buf[CC33XX_CMD_BUFFER_SIZE];
+
+ /* number of keys requiring extra spare mem-blocks */
+ int extra_spare_key_count;
+
+ u8 efuse_mac_address[ETH_ALEN];
+
+ u32 fuse_rom_structure_version;
+ u32 device_part_number;
+ u32 pg_version;
+ u8 disable_5g;
+ u8 disable_6g;
+
+ struct driver_fw_versions all_versions;
+
+ u8 antenna_selection;
+
+ /* burst mode cfg */
+ u8 burst_disable;
+
+ struct cc33xx_ant_diversity diversity;
+};
+
+void cc33xx_update_inconn_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct cc33xx_station *wl_sta, bool in_conn);
+
+void cc33xx_irq(void *cookie);
+
+/* Quirks */
+
+/* the first start_role(sta) sometimes doesn't work on wl12xx */
+#define CC33XX_QUIRK_START_STA_FAILS BIT(1)
+
+/* wl127x and SPI don't support SDIO block size alignment */
+#define CC33XX_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2)
+
+/* means aggregated Rx packets are aligned to a SDIO block */
+#define CC33XX_QUIRK_RX_BLOCKSIZE_ALIGN BIT(3)
+
+/* pad only the last frame in the aggregate buffer */
+#define CC33XX_QUIRK_TX_PAD_LAST_FRAME BIT(7)
+
+/* extra header space is required for TKIP */
+#define CC33XX_QUIRK_TKIP_HEADER_SPACE BIT(8)
+
+/* Some firmwares not support sched scans while connected */
+#define CC33XX_QUIRK_NO_SCHED_SCAN_WHILE_CONN BIT(9)
+
+/* separate probe response templates for one-shot and sched scans */
+#define CC33XX_QUIRK_DUAL_PROBE_TMPL BIT(10)
+
+/* Firmware requires reg domain configuration for active calibration */
+#define CC33XX_QUIRK_REGDOMAIN_CONF BIT(11)
+
+/* The FW only support a zero session id for AP */
+#define CC33XX_QUIRK_AP_ZERO_SESSION_ID BIT(12)
+
+/* TODO: move all these common registers and values elsewhere */
+#define HW_ACCESS_ELP_CTRL_REG 0x1FFFC
+
+enum CC33xx_FRAME_FORMAT {
+ CC33xx_B_SHORT = 0,
+ CC33xx_B_LONG,
+ CC33xx_LEGACY_OFDM,
+ CC33xx_HT_MF,
+ CC33xx_HT_GF,
+ CC33xx_HE_SU,
+ CC33xx_HE_MU,
+ CC33xx_HE_SU_ER,
+ CC33xx_HE_TB,
+ CC33xx_HE_TB_NDP_FB,
+ CC33xx_VHT
+};
+
+/* CC33xx HW Common Definitions */
+
+#define HOST_SYNC_PATTERN 0x5C5C5C5C
+#define DEVICE_SYNC_PATTERN 0xABCDDCBA
+#define NAB_DATA_ADDR 0x0000BFF0
+#define NAB_CONTROL_ADDR 0x0000BFF8
+#define NAB_STATUS_ADDR 0x0000BFFC
+
+#define NAB_SEND_CMD 0x940d
+#define NAB_SEND_FLAGS 0x08
+#define CC33xx_INTERNAL_DESC_SIZE 200
+#define NAB_EXTRA_BYTES 4
+
+#define TX_RESULT_QUEUE_SIZE 108
+
+struct control_info_descriptor {
+ __le16 type_and_length;
+};
+
+enum control_message_type {
+ CTRL_MSG_NONE = 0,
+ CTRL_MSG_EVENT = 1,
+ CTRL_MSG_COMMND_COMPLETE = 2
+};
+
+struct core_fw_status {
+ u8 tx_result_queue_index;
+ u8 reserved1[3];
+ u8 tx_result_queue[TX_RESULT_QUEUE_SIZE];
+
+ /* A bitmap (where each bit represents a single HLID)
+ * to indicate PS/Active mode of the link
+ */
+ __le32 link_ps_bitmap;
+
+ /* A bitmap (where each bit represents a single HLID)
+ * to indicate if the station is in Fast mode
+ */
+ __le32 link_fast_bitmap;
+
+ /* A bitmap (where each bit represents a single HLID)
+ * to indicate if a links is suspended/aboout to be suspended
+ */
+ __le32 link_suspend_bitmap;
+
+ /* Host TX Flow Control descriptor per AC threshold */
+ u8 tx_flow_control_ac_threshold;
+
+ /* Host TX Flow Control descriptor PS link threshold */
+ u8 tx_ps_threshold;
+
+ /* Host TX Flow Control descriptor Suspended link threshold */
+ u8 tx_suspend_threshold;
+
+ /* Host TX Flow Control descriptor Slow link threshold */
+ u8 tx_slow_link_prio_threshold;
+
+ /* Host TX Flow Control descriptor Fast link threshold */
+ u8 tx_fast_link_prio_threshold;
+
+ /* Host TX Flow Control descriptor Stop Slow link threshold */
+ u8 tx_slow_stop_threshold;
+
+ /* Host TX Flow Control descriptor Stop Fast link threshold */
+ u8 tx_fast_stop_threshold;
+
+ u8 reserved2;
+ /* Additional information can be added here */
+} __packed;
+
+struct core_status {
+ __le32 block_pad[28];
+ __le32 host_interrupt_status;
+ __le32 rx_status;
+ struct core_fw_status fw_info;
+ __le32 tsf;
+} __packed;
+
+struct NAB_header {
+ __le32 sync_pattern;
+ __le16 opcode;
+ __le16 len;
+};
+
+/* rx_status lower bytes hold the rx byte count */
+#define RX_BYTE_COUNT_MASK 0xFFFF
+
+#define HINT_NEW_TX_RESULT 0x1
+#define HINT_COMMAND_COMPLETE 0x2
+#define HINT_ROM_LOADER_INIT_COMPLETE 0x8
+#define HINT_SECOND_LOADER_INIT_COMPLETE 0x10
+#define HINT_FW_WAKEUP_COMPLETE 0x20
+#define HINT_FW_INIT_COMPLETE 0x40
+#define HINT_GENERAL_ERROR 0x80000000
+
+#define BOOT_TIME_INTERRUPTS (\
+ HINT_ROM_LOADER_INIT_COMPLETE | \
+ HINT_SECOND_LOADER_INIT_COMPLETE | \
+ HINT_FW_WAKEUP_COMPLETE | \
+ HINT_FW_INIT_COMPLETE)
+
+struct NAB_tx_header {
+ __le32 sync;
+ __le16 opcode;
+ __le16 len;
+ __le16 desc_length;
+ u8 sd;
+ u8 flags;
+} __packed;
+
+struct NAB_rx_header {
+ __le32 cnys;
+ __le16 opcode;
+ __le16 len;
+ __le32 rx_desc;
+ __le32 reserved;
+} __packed;
+
+#endif /* __CC33XX_H__ */
diff --git a/drivers/net/wireless/ti/cc33xx/cc33xx_i.h b/drivers/net/wireless/ti/cc33xx/cc33xx_i.h
new file mode 100644
index 000000000000..9437f78b364f
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/cc33xx_i.h
@@ -0,0 +1,459 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __CC33XX_I_H__
+#define __CC33XX_I_H__
+
+#include <net/mac80211.h>
+#include <linux/platform_device.h>
+
+#include "conf.h"
+
+struct cc33xx_family_data {
+ const char *name;
+ const char *nvs_name; /* nvs file */
+ const char *cfg_name; /* cfg file */
+};
+
+#define CC33XX_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
+#define CC33XX_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
+#define CC33XX_TX_SQN_POST_RECOVERY_PADDING 0xff
+/* Use smaller padding for GEM, as some APs have issues when it's too big */
+#define CC33XX_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20
+
+#define CC33XX_CIPHER_SUITE_GEM 0x00147201
+
+#define CC33XX_BUSY_WORD_LEN (sizeof(u32))
+
+#define CC33XX_DEFAULT_BEACON_INT 100
+
+#define CC33XX_MAX_ROLES 4
+#define CC33XX_INVALID_ROLE_ID 0xff
+#define CC33XX_INVALID_LINK_ID 0xff
+
+#define CC33XX_MAX_LINKS 21
+
+/* the driver supports the 2.4Ghz and 5Ghz bands */
+#define CC33XX_NUM_BANDS 2
+
+#define CC33XX_MAX_RATE_POLICIES 16
+
+/* Defined by FW as 0. Will not be freed or allocated. */
+#define CC33XX_SYSTEM_HLID 0
+
+/* When in AP-mode, we allow (at least) this number of packets
+ * to be transmitted to FW for a STA in PS-mode. Only when packets are
+ * present in the FW buffers it will wake the sleeping STA. We want to put
+ * enough packets for the driver to transmit all of its buffered data before
+ * the STA goes to sleep again. But we don't want to take too much memory
+ * as it might hurt the throughput of active STAs.
+ */
+#define CC33XX_PS_STA_MAX_PACKETS 2
+
+#define CC33XX_AP_BSS_INDEX 0
+
+enum cc33xx_state {
+ CC33XX_STATE_OFF,
+ CC33XX_STATE_RESTARTING,
+ CC33XX_STATE_ON,
+};
+
+struct cc33xx;
+
+#define NUM_TX_QUEUES 4
+
+#define CC33XX_MAX_CHANNELS 64
+struct cc33xx_scan {
+ struct cfg80211_scan_request *req;
+ unsigned long scanned_ch[BITS_TO_LONGS(CC33XX_MAX_CHANNELS)];
+ bool failed;
+ u8 state;
+ u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+ size_t ssid_len;
+};
+
+struct cc33xx_if_operations {
+ void (*interface_claim)(struct device *child);
+ void (*interface_release)(struct device *child);
+ int __must_check (*read)(struct device *child, int addr, void *buf,
+ size_t len, bool fixed);
+ int __must_check (*write)(struct device *child, int addr, void *buf,
+ size_t len, bool fixed);
+ void (*reset)(struct device *child);
+ void (*init)(struct device *child);
+ int (*power)(struct device *child, bool enable);
+ void (*set_block_size)(struct device *child, unsigned int blksz);
+ size_t (*get_max_transaction_len)(struct device *child);
+ void (*set_irq_handler)(struct device *child, void *irq_handler);
+ void (*enable_irq)(struct device *child);
+ void (*disable_irq)(struct device *child);
+};
+
+struct cc33xx_platdev_data {
+ struct cc33xx_if_operations *if_ops;
+ const struct cc33xx_family_data *family;
+ void (*irq_handler)(struct platform_device *pdev);
+ int gpio_irq_num;
+
+ bool ref_clock_xtal; /* specify whether the clock is XTAL or not */
+ bool pwr_in_suspend;
+};
+
+#define MAX_NUM_KEYS 14
+#define MAX_KEY_SIZE 32
+
+struct cc33xx_ap_key {
+ u8 id;
+ u8 key_type;
+ u8 key_size;
+ u8 key[MAX_KEY_SIZE];
+ u8 hlid;
+ u32 tx_seq_32;
+ u16 tx_seq_16;
+};
+
+enum cc33xx_flags {
+ CC33XX_FLAG_GPIO_POWER,
+ CC33XX_FLAG_TX_PENDING,
+ CC33XX_FLAG_IN_ELP,
+ CC33XX_FLAG_FW_TX_BUSY,
+ CC33XX_FLAG_DUMMY_PACKET_PENDING,
+ CC33XX_FLAG_SUSPENDED,
+ CC33XX_FLAG_PENDING_WORK,
+ CC33XX_FLAG_SOFT_GEMINI,
+ CC33XX_FLAG_DRIVER_REMOVED,
+ CC33XX_FLAG_RECOVERY_IN_PROGRESS,
+ CC33XX_FLAG_VIF_CHANGE_IN_PROGRESS,
+ CC33XX_FLAG_IO_FAILED,
+ CC33XX_FLAG_REINIT_TX_WDOG,
+};
+
+enum cc33xx_vif_flags {
+ WLVIF_FLAG_INITIALIZED,
+ WLVIF_FLAG_STA_ASSOCIATED,
+ WLVIF_FLAG_STA_AUTHORIZED,
+ WLVIF_FLAG_IBSS_JOINED,
+ WLVIF_FLAG_AP_STARTED,
+ WLVIF_FLAG_IN_PS,
+ WLVIF_FLAG_STA_STATE_SENT,
+ WLVIF_FLAG_PSPOLL_FAILURE,
+ WLVIF_FLAG_CS_PROGRESS,
+ WLVIF_FLAG_AP_PROBE_RESP_SET,
+ WLVIF_FLAG_IN_USE,
+ WLVIF_FLAG_ACTIVE,
+ WLVIF_FLAG_BEACON_DISABLED,
+};
+
+struct cc33xx_vif;
+
+struct cc33xx_link {
+ /* AP-mode - TX queue per AC in link */
+ struct sk_buff_head tx_queue[NUM_TX_QUEUES];
+
+ /* accounting for allocated / freed packets in FW */
+ u8 allocated_pkts;
+ u8 prev_freed_pkts;
+
+ u8 addr[ETH_ALEN];
+
+ /* bitmap of TIDs where RX BA sessions are active for this link */
+ u8 ba_bitmap;
+
+ /* the last fw rate index we used for this link */
+ u8 fw_rate_idx;
+
+ /* the last fw rate [Mbps] we used for this link */
+ u8 fw_rate_mbps;
+
+ /* The wlvif this link belongs to. Might be null for global links */
+ struct cc33xx_vif *wlvif;
+
+ /* total freed FW packets on the link - used for tracking the
+ * AES/TKIP PN across recoveries. Re-initialized each time
+ * from the cc33xx_station structure.
+ */
+ u64 total_freed_pkts;
+};
+
+#define CC33XX_MAX_RX_FILTERS 5
+#define CC33XX_RX_FILTER_MAX_FIELDS 8
+
+#define CC33XX_RX_FILTER_ETH_HEADER_SIZE 14
+#define CC33XX_RX_FILTER_MAX_FIELDS_SIZE 95
+#define RX_FILTER_FIELD_OVERHEAD \
+ (sizeof(struct cc33xx_rx_filter_field) - sizeof(u8 *))
+#define CC33XX_RX_FILTER_MAX_PATTERN_SIZE \
+ (CC33XX_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD)
+
+#define CC33XX_RX_FILTER_FLAG_IP_HEADER 0
+#define CC33XX_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1)
+
+struct ieee80211_header {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 payload[];
+} __packed;
+
+enum rx_filter_action {
+ FILTER_DROP = 0,
+ FILTER_SIGNAL = 1,
+ FILTER_FW_HANDLE = 2
+};
+
+enum plt_mode {
+ PLT_OFF = 0,
+ PLT_ON = 1,
+ PLT_FEM_DETECT = 2,
+ PLT_CHIP_AWAKE = 3
+};
+
+struct cc33xx_rx_filter_field {
+ __le16 offset;
+ u8 len;
+ u8 flags;
+ u8 *pattern;
+} __packed;
+
+struct cc33xx_rx_filter {
+ u8 action;
+ int num_fields;
+ struct cc33xx_rx_filter_field fields[CC33XX_RX_FILTER_MAX_FIELDS];
+};
+
+struct cc33xx_station {
+ u8 hlid;
+ bool in_connection;
+
+ /* total freed FW packets on the link to the STA - used for tracking the
+ * AES/TKIP PN across recoveries. Re-initialized each time from the
+ * cc33xx_station structure.
+ * Used in both AP and STA mode.
+ */
+ u64 total_freed_pkts;
+};
+
+struct cc33xx_vif {
+ struct cc33xx *cc;
+ struct list_head list;
+ unsigned long flags;
+ u8 bss_type;
+ u8 p2p; /* we are using p2p role */
+ u8 role_id;
+
+ /* sta/ibss specific */
+ u8 dev_role_id;
+ u8 dev_hlid;
+
+ union {
+ struct {
+ u8 hlid;
+
+ u8 basic_rate_idx;
+ u8 ap_rate_idx;
+ u8 p2p_rate_idx;
+
+ bool qos;
+ /* channel type we started the STA role with */
+ enum nl80211_channel_type role_chan_type;
+ } sta;
+ struct {
+ u8 global_hlid;
+ u8 bcast_hlid;
+
+ /* HLIDs bitmap of associated stations */
+ unsigned long sta_hlid_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)];
+
+ /* recoreded keys - set here before AP startup */
+ struct cc33xx_ap_key *recorded_keys[MAX_NUM_KEYS];
+
+ u8 mgmt_rate_idx;
+ u8 bcast_rate_idx;
+ u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT];
+ } ap;
+ };
+
+ /* the hlid of the last transmitted skb */
+ int last_tx_hlid;
+
+ /* counters of packets per AC, across all links in the vif */
+ int tx_queue_count[NUM_TX_QUEUES];
+
+ unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)];
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 ssid_len;
+
+ /* The current band */
+ enum nl80211_band band;
+ int channel;
+ enum nl80211_channel_type channel_type;
+
+ u32 bitrate_masks[CC33XX_NUM_BANDS];
+ u32 basic_rate_set;
+
+ /* currently configured rate set:
+ * bits 0-15 - 802.11abg rates
+ * bits 16-23 - 802.11n MCS index mask
+ * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
+ */
+ u32 basic_rate;
+ u32 rate_set;
+
+ /* probe-req template for the current AP */
+ struct sk_buff *probereq;
+
+ /* Beaconing interval (needed for ad-hoc) */
+ u32 beacon_int;
+
+ /* Default key (for WEP) */
+ u32 default_key;
+
+ /* Our association ID */
+ u16 aid;
+
+ /* retry counter for PSM entries */
+ u8 psm_entry_retry;
+
+ /* in dBm */
+ int power_level;
+
+ int rssi_thold;
+ int last_rssi_event;
+
+ /* save the current encryption type for auto-arp config */
+ u8 encryption_type;
+ __be32 ip_addr;
+
+ /* RX BA constraint value */
+ bool ba_support;
+ bool ba_allowed;
+
+ bool wmm_enabled;
+
+ bool radar_enabled;
+
+ struct delayed_work channel_switch_work;
+ struct delayed_work connection_loss_work;
+
+ /* number of in connection stations */
+ int inconn_count;
+
+ /* This vif's queues are mapped to mac80211 HW queues as:
+ * VO - hw_queue_base
+ * VI - hw_queue_base + 1
+ * BE - hw_queue_base + 2
+ * BK - hw_queue_base + 3
+ */
+ int hw_queue_base;
+
+ /* do we have a pending auth reply? (and ROC) */
+ bool ap_pending_auth_reply;
+
+ /* time when we sent the pending auth reply */
+ unsigned long pending_auth_reply_time;
+
+ /* work for canceling ROC after pending auth reply */
+ struct delayed_work pending_auth_complete_work;
+
+ struct delayed_work roc_timeout_work;
+
+ /* update rate conrol */
+ enum ieee80211_sta_rx_bandwidth rc_update_bw;
+ struct ieee80211_sta_ht_cap rc_ht_cap;
+ struct work_struct rc_update_work;
+
+ /* total freed FW packets on the link.
+ * For STA this holds the PN of the link to the AP.
+ * For AP this holds the PN of the broadcast link.
+ */
+ u64 total_freed_pkts;
+
+ /* for MBSSID: this BSS is a nontransmitted BSS profile
+ * Relevant for STA role
+ */
+ bool nontransmitted;
+
+ /* for MBSSID: update transmitter BSSID */
+ u8 transmitter_bssid[ETH_ALEN];
+
+ /* for MBSSID: BSSID index */
+ u8 bssid_index;
+
+ /* for MBSSID: BSSID indicator */
+ u8 bssid_indicator;
+
+ /* for STA: if connection established and has HE support*/
+ u8 sta_has_he;
+
+ /* This struct must be last!
+ * data that has to be saved acrossed reconfigs (e.g. recovery)
+ * should be declared in this struct.
+ */
+ u8 persistent[];
+};
+
+static inline struct cc33xx_vif *cc33xx_vif_to_data(struct ieee80211_vif *vif)
+{
+ WARN_ON(!vif);
+ return (struct cc33xx_vif *)vif->drv_priv;
+}
+
+static inline
+struct ieee80211_vif *cc33xx_wlvif_to_vif(struct cc33xx_vif *wlvif)
+{
+ return container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
+}
+
+static inline bool cc33xx_is_p2p_mgmt(struct cc33xx_vif *wlvif)
+{
+ return cc33xx_wlvif_to_vif(wlvif)->type == NL80211_IFTYPE_P2P_DEVICE;
+}
+
+#define cc33xx_for_each_wlvif(cc, wlvif) \
+ list_for_each_entry(wlvif, &(cc)->wlvif_list, list)
+
+#define cc33xx_for_each_wlvif_continue(cc, wlvif) \
+ list_for_each_entry_continue(wlvif, &(cc)->wlvif_list, list)
+
+#define cc33xx_for_each_wlvif_bss_type(cc, wlvif, _bss_type) \
+ cc33xx_for_each_wlvif((cc), (wlvif)) \
+ if ((wlvif)->bss_type == (_bss_type))
+
+#define cc33xx_for_each_wlvif_sta(cc, wlvif) \
+ cc33xx_for_each_wlvif_bss_type(cc, wlvif, BSS_TYPE_STA_BSS)
+
+#define cc33xx_for_each_wlvif_ap(cc, wlvif) \
+ cc33xx_for_each_wlvif_bss_type(cc, wlvif, BSS_TYPE_AP_BSS)
+
+int cc33xx_plt_start(struct cc33xx *cc, const enum plt_mode plt_mode);
+int cc33xx_plt_stop(struct cc33xx *cc);
+void cc33xx_queue_recovery_work(struct cc33xx *cc);
+void cc33xx_flush_deferred_work(struct cc33xx *cc);
+
+#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */
+
+#define CC33XX_MAX_TXPWR 21 /* maximum power limit is 21dBm */
+#define CC33XX_MIN_TXPWR -10 /* minmum power limit is -10dBm */
+
+#define CC33XX_TX_QUEUE_LOW_WATERMARK 32
+#define CC33XX_TX_QUEUE_HIGH_WATERMARK 256
+
+#define CC33XX_RX_QUEUE_MAX_LEN 256
+
+/* cc33xx needs a 200ms sleep after power on, and a 20ms sleep before power
+ * on in case is has been shut down shortly before
+ */
+#define CC33XX_PRE_POWER_ON_SLEEP 20 /* in milliseconds */
+#define CC33XX_POWER_ON_SLEEP 200 /* in milliseconds */
+
+/* Macros to handle cc33xx.sta_rate_set */
+#define HW_HT_RATES_OFFSET 16
+#define HW_MIMO_RATES_OFFSET 24
+
+#endif /* __CC33XX_I_H__ */
--
2.25.1


2024-05-21 17:21:22

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 16/17] Add Kconfig, Makefile and integrate into wireless/ti folder

From: Michael Nemanov <[email protected]>

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/Kconfig | 1 +
drivers/net/wireless/ti/Makefile | 1 +
drivers/net/wireless/ti/cc33xx/Kconfig | 24 ++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/Makefile | 10 ++++++++++
4 files changed, 36 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/Kconfig
create mode 100644 drivers/net/wireless/ti/cc33xx/Makefile

diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig
index 3fcd9e395f72..fa7214d6018c 100644
--- a/drivers/net/wireless/ti/Kconfig
+++ b/drivers/net/wireless/ti/Kconfig
@@ -14,6 +14,7 @@ if WLAN_VENDOR_TI
source "drivers/net/wireless/ti/wl1251/Kconfig"
source "drivers/net/wireless/ti/wl12xx/Kconfig"
source "drivers/net/wireless/ti/wl18xx/Kconfig"
+source "drivers/net/wireless/ti/cc33xx/Kconfig"

# keep last for automatic dependencies
source "drivers/net/wireless/ti/wlcore/Kconfig"
diff --git a/drivers/net/wireless/ti/Makefile b/drivers/net/wireless/ti/Makefile
index 05ee016594f8..9e028a91ec30 100644
--- a/drivers/net/wireless/ti/Makefile
+++ b/drivers/net/wireless/ti/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_WLCORE) += wlcore/
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_WL1251) += wl1251/
obj-$(CONFIG_WL18XX) += wl18xx/
+obj-$(CONFIG_CC33XX) += cc33xx/
\ No newline at end of file
diff --git a/drivers/net/wireless/ti/cc33xx/Kconfig b/drivers/net/wireless/ti/cc33xx/Kconfig
new file mode 100644
index 000000000000..0c3ff97dacc7
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config CC33XX
+ tristate "TI CC33XX support"
+ depends on MAC80211
+ select FW_LOADER
+ help
+ This module contains the main code for TI CC33XX WLAN chips. It abstracts
+ hardware-specific differences among different chipset families.
+ Each chipset family needs to implement its own lower-level module
+ that will depend on this module for the common code.
+
+ If you choose to build a module, it will be called cc33xx. Say N if
+ unsure.
+
+config CC33XX_SDIO
+ tristate "TI CC33XX SDIO support"
+ depends on CC33XX && MMC
+ help
+ This module adds support for the SDIO interface of adapters using
+ TI CC33XX WLAN chipsets. Select this if your platform is using
+ the SDIO bus.
+
+ If you choose to build a module, it'll be called cc33xx_sdio.
+ Say N if unsure.
diff --git a/drivers/net/wireless/ti/cc33xx/Makefile b/drivers/net/wireless/ti/cc33xx/Makefile
new file mode 100644
index 000000000000..6156f778edee
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cc33xx-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
+ boot.o init.o scan.o
+
+cc33xx_sdio-objs = sdio.o
+
+cc33xx-$(CONFIG_NL80211_TESTMODE) += testmode.o
+obj-$(CONFIG_CC33XX) += cc33xx.o
+obj-$(CONFIG_CC33XX_SDIO) += cc33xx_sdio.o
--
2.25.1


2024-05-21 17:21:34

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 11/17] Add init.c, init.h

From: Michael Nemanov <[email protected]>

High-level init code for new vifs

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/init.c | 236 ++++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/init.h | 15 ++
2 files changed, 251 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/init.c
create mode 100644 drivers/net/wireless/ti/cc33xx/init.h

diff --git a/drivers/net/wireless/ti/cc33xx/init.c b/drivers/net/wireless/ti/cc33xx/init.c
new file mode 100644
index 000000000000..f338901ea96d
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/init.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/firmware.h>
+#include "acx.h"
+#include "cmd.h"
+#include "conf.h"
+#include "event.h"
+#include "tx.h"
+#include "init.h"
+
+static int cc33xx_init_phy_vif_config(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif)
+{
+ int ret;
+
+ ret = cc33xx_acx_slot(cc, wlvif, DEFAULT_SLOT_TIME);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cc33xx_init_sta_beacon_filter(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif)
+{
+ int ret;
+
+ ret = cc33xx_acx_beacon_filter_table(cc, wlvif);
+ if (ret < 0)
+ return ret;
+
+ /* disable beacon filtering until we get the first beacon */
+ ret = cc33xx_acx_beacon_filter_opt(cc, wlvif, false);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cc33xx_ap_init_templates(struct cc33xx *cc,
+ struct ieee80211_vif *vif)
+{
+ struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+ int ret;
+
+ /* when operating as AP we want to receive external beacons for
+ * configuring ERP protection.
+ */
+ ret = cc33xx_acx_beacon_filter_opt(cc, wlvif, false);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void cc33xx_set_ba_policies(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ /* Reset the BA RX indicators */
+ wlvif->ba_allowed = true;
+ cc->ba_rx_session_count = 0;
+
+ /* BA is supported in STA/AP modes */
+ wlvif->ba_support = (wlvif->bss_type != BSS_TYPE_AP_BSS &&
+ wlvif->bss_type != BSS_TYPE_STA_BSS);
+}
+
+/* vif-specifc initialization */
+static int cc33xx_init_sta_role(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ int ret = cc33xx_acx_group_address_tbl(cc, true, NULL, 0);
+
+ if (ret < 0)
+ return ret;
+
+ /* Beacon filtering */
+ ret = cc33xx_init_sta_beacon_filter(cc, wlvif);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* vif-specific initialization */
+static int cc33xx_init_ap_role(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ int ret;
+
+ /* initialize Tx power */
+ ret = cc33xx_acx_tx_power(cc, wlvif, wlvif->power_level);
+ if (ret < 0)
+ return ret;
+
+ if (cc->radar_debug_mode)
+ cc33xx_cmd_generic_cfg(cc, wlvif,
+ CC33XX_CFG_FEATURE_RADAR_DEBUG,
+ cc->radar_debug_mode, 0);
+
+ return 0;
+}
+
+int cc33xx_init_vif_specific(struct cc33xx *cc, struct ieee80211_vif *vif)
+{
+ struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+ struct conf_tx_ac_category *conf_ac;
+ struct conf_tx_ac_category ac_conf[4];
+ struct conf_tx_tid tid_conf[8];
+ struct conf_tx_settings *tx_settings = &cc->conf.host_conf.tx;
+ struct conf_tx_ac_category *p_wl_host_ac_conf = &tx_settings->ac_conf0;
+ struct conf_tx_tid *p_wl_host_tid_conf = &tx_settings->tid_conf0;
+ bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+ u8 ps_scheme = cc->conf.mac.ps_scheme;
+ int ret, i;
+
+ /* consider all existing roles before configuring psm. */
+
+ if (cc->ap_count == 0 && is_ap) { /* first AP */
+ ret = cc33xx_acx_sleep_auth(cc, CC33XX_PSM_ELP);
+ if (ret < 0)
+ return ret;
+
+ /* unmask ap events */
+ cc->event_mask |= cc->ap_event_mask;
+
+ /* first STA, no APs */
+ } else if (cc->sta_count == 0 && cc->ap_count == 0 && !is_ap) {
+ u8 sta_auth = cc->conf.host_conf.conn.sta_sleep_auth;
+ /* Configure for power according to debugfs */
+ if (sta_auth != CC33XX_PSM_ILLEGAL)
+ ret = cc33xx_acx_sleep_auth(cc, sta_auth);
+ /* Configure for ELP power saving */
+ else
+ ret = cc33xx_acx_sleep_auth(cc, CC33XX_PSM_ELP);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Mode specific init */
+ if (is_ap) {
+ ret = cc33xx_init_ap_role(cc, wlvif);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = cc33xx_init_sta_role(cc, wlvif);
+ if (ret < 0)
+ return ret;
+ }
+
+ cc33xx_init_phy_vif_config(cc, wlvif);
+
+ /* Default TID/AC configuration */
+ WARN_ON(tx_settings->tid_conf_count != tx_settings->ac_conf_count);
+ memcpy(ac_conf, p_wl_host_ac_conf, 4 * sizeof(struct conf_tx_ac_category));
+ memcpy(tid_conf, p_wl_host_tid_conf, 8 * sizeof(struct conf_tx_tid));
+
+ for (i = 0; i < tx_settings->tid_conf_count; i++) {
+ conf_ac = &ac_conf[i];
+
+ /* If no ps poll is used, send legacy ps scheme in cmd */
+ if (ps_scheme == PS_SCHEME_NOPSPOLL)
+ ps_scheme = PS_SCHEME_LEGACY;
+
+ ret = cc33xx_tx_param_cfg(cc, wlvif, conf_ac->ac,
+ conf_ac->cw_min, conf_ac->cw_max,
+ conf_ac->aifsn, conf_ac->tx_op_limit,
+ false, ps_scheme, conf_ac->is_mu_edca,
+ conf_ac->mu_edca_aifs,
+ conf_ac->mu_edca_ecw_min_max,
+ conf_ac->mu_edca_timer);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Mode specific init - post mem init */
+ if (is_ap)
+ ret = cc33xx_ap_init_templates(cc, vif);
+
+ if (ret < 0)
+ return ret;
+
+ /* Configure initiator BA sessions policies */
+ cc33xx_set_ba_policies(cc, wlvif);
+
+ return 0;
+}
+
+int cc33xx_hw_init(struct cc33xx *cc)
+{
+ cc33xx_acx_init_mem_config(cc);
+
+ cc33xx_debug(DEBUG_TX, "available tx blocks: %d", 16);
+ cc->last_fw_rls_idx = 0;
+ cc->partial_rx.status = CURR_RX_START;
+ return 0;
+}
+
+int cc33xx_download_ini_params_and_wait(struct cc33xx *cc)
+{
+ struct cc33xx_cmd_ini_params_download *cmd;
+ size_t command_size = ALIGN((sizeof(*cmd) + sizeof(cc->conf)), 4);
+ int ret;
+
+ cc33xx_set_max_buffer_size(cc, INI_MAX_BUFFER_SIZE);
+
+ cc33xx_debug(DEBUG_ACX,
+ "Downloading INI configurations to FW, payload Length: %zu",
+ sizeof(cc->conf));
+
+ cmd = kzalloc(command_size, GFP_KERNEL);
+ if (!cmd) {
+ cc33xx_set_max_buffer_size(cc, CMD_MAX_BUFFER_SIZE);
+ return -ENOMEM;
+ }
+
+ cmd->length = cpu_to_le32(sizeof(cc->conf));
+
+ /* copy INI file params payload */
+ memcpy((cmd->payload), &cc->conf, sizeof(cc->conf));
+
+ ret = cc33xx_cmd_send(cc, CMD_DOWNLOAD_INI_PARAMS,
+ cmd, command_size, 0);
+ if (ret < 0) {
+ cc33xx_warning("download INI params to FW command sending failed: %d",
+ ret);
+ } else {
+ cc33xx_debug(DEBUG_BOOT, "INI Params downloaded successfully");
+ }
+
+ cc33xx_set_max_buffer_size(cc, CMD_MAX_BUFFER_SIZE);
+ kfree(cmd);
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/cc33xx/init.h b/drivers/net/wireless/ti/cc33xx/init.h
new file mode 100644
index 000000000000..b0bc6a548611
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/init.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __INIT_H__
+#define __INIT_H__
+
+#include "cc33xx.h"
+
+int cc33xx_hw_init(struct cc33xx *cc);
+int cc33xx_download_ini_params_and_wait(struct cc33xx *cc);
+int cc33xx_init_vif_specific(struct cc33xx *cc, struct ieee80211_vif *vif);
+
+#endif /* __INIT_H__ */
--
2.25.1


2024-05-21 17:21:50

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 10/17] Add tx.c, tx.h

From: Michael Nemanov <[email protected]>

Tx of data frames starts either with MAC80211 tx op (cc33xx_op_tx)
or from the deferred IRQ context (irq_deferred_work). Both trigger
cc->tx_work (cc33xx_tx_work @ tx.c) which will call
cc33xx_tx_work_locked where data from MAC80211 SKBs will be packed
and transferred to HW. An interrupt will then be received from HW
indicating that a given frame was transmitted or expired so that its
SKB can be freed (cc33xx_tx_immediate_complete).

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/tx.c | 1416 +++++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/tx.h | 160 +++
2 files changed, 1576 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/tx.c
create mode 100644 drivers/net/wireless/ti/cc33xx/tx.h

diff --git a/drivers/net/wireless/ti/cc33xx/tx.c b/drivers/net/wireless/ti/cc33xx/tx.c
new file mode 100644
index 000000000000..3a9bbd888164
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/tx.c
@@ -0,0 +1,1416 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include "acx.h"
+#include "debug.h"
+#include "io.h"
+#include "ps.h"
+#include "tx.h"
+#include "cc33xx.h"
+
+static int cc33xx_set_default_wep_key(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 id)
+{
+ int ret;
+ bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+
+ if (is_ap)
+ ret = cc33xx_cmd_set_default_wep_key(cc, id,
+ wlvif->ap.bcast_hlid);
+ else
+ ret = cc33xx_cmd_set_default_wep_key(cc, id, wlvif->sta.hlid);
+
+ if (ret < 0)
+ return ret;
+
+ cc33xx_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id);
+ return 0;
+}
+
+static int cc33xx_alloc_tx_id(struct cc33xx *cc, struct sk_buff *skb)
+{
+ int id;
+
+ id = find_first_zero_bit(cc->tx_frames_map, CC33XX_NUM_TX_DESCRIPTORS);
+ if (id >= CC33XX_NUM_TX_DESCRIPTORS)
+ return -EBUSY;
+
+ __set_bit(id, cc->tx_frames_map);
+ cc->tx_frames[id] = skb;
+ cc->tx_frames_cnt++;
+ cc33xx_debug(DEBUG_TX, "alloc desc ID. id - %d, frames count %d",
+ id, cc->tx_frames_cnt);
+ return id;
+}
+
+void cc33xx_free_tx_id(struct cc33xx *cc, int id)
+{
+ if (__test_and_clear_bit(id, cc->tx_frames_map)) {
+ if (unlikely(cc->tx_frames_cnt == CC33XX_NUM_TX_DESCRIPTORS))
+ clear_bit(CC33XX_FLAG_FW_TX_BUSY, &cc->flags);
+
+ cc->tx_frames[id] = NULL;
+ cc->tx_frames_cnt--;
+ }
+ cc33xx_debug(DEBUG_TX, "free desc ID. id - %d, frames count %d",
+ id, cc->tx_frames_cnt);
+}
+EXPORT_SYMBOL(cc33xx_free_tx_id);
+
+static void cc33xx_tx_ap_update_inconnection_sta(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)(skb->data +
+ sizeof(struct cc33xx_tx_hw_descr));
+ if (!ieee80211_is_auth(hdr->frame_control))
+ return;
+
+ /* ROC for 1 second on the AP channel for completing the connection.
+ * Note the ROC will be continued by the update_sta_state callbacks
+ * once the station reaches the associated state.
+ */
+ cc33xx_update_inconn_sta(cc, wlvif, NULL, true);
+ wlvif->pending_auth_reply_time = jiffies;
+ cancel_delayed_work(&wlvif->pending_auth_complete_work);
+ ieee80211_queue_delayed_work(cc->hw,
+ &wlvif->pending_auth_complete_work,
+ msecs_to_jiffies(CC33XX_PEND_AUTH_ROC_TIMEOUT));
+}
+
+static void cc33xx_tx_regulate_link(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif,
+ u8 hlid)
+{
+ bool fw_ps;
+ u8 tx_pkts;
+
+ if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
+ return;
+
+ fw_ps = test_bit(hlid, &cc->ap_fw_ps_map);
+ tx_pkts = cc->links[hlid].allocated_pkts;
+
+ /* if in FW PS and there is enough data in FW we can put the link
+ * into high-level PS and clean out its TX queues.
+ * Make an exception if this is the only connected link. In this
+ * case FW-memory congestion is less of a problem.
+ * Note that a single connected STA means 2*ap_count + 1 active links,
+ * since we must account for the global and broadcast AP links
+ * for each AP. The "fw_ps" check assures us the other link is a STA
+ * connected to the AP. Otherwise the FW would not set the PSM bit.
+ */
+ if (cc->active_link_count > (cc->ap_count * 2 + 1) && fw_ps &&
+ tx_pkts >= CC33XX_PS_STA_MAX_PACKETS)
+ cc33xx_ps_link_start(cc, wlvif, hlid, true);
+}
+
+inline bool cc33xx_is_dummy_packet(struct cc33xx *cc, struct sk_buff *skb)
+{
+ return cc->dummy_packet == skb;
+}
+EXPORT_SYMBOL(cc33xx_is_dummy_packet);
+
+static u8 cc33xx_tx_get_hlid_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+ struct ieee80211_hdr *hdr;
+
+ if (sta) {
+ struct cc33xx_station *wl_sta;
+
+ wl_sta = (struct cc33xx_station *)sta->drv_priv;
+ return wl_sta->hlid;
+ }
+
+ if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
+ return CC33XX_SYSTEM_HLID;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ if (is_multicast_ether_addr(ieee80211_get_DA(hdr)))
+ return wlvif->ap.bcast_hlid;
+ else
+ return wlvif->ap.global_hlid;
+}
+
+u8 cc33xx_tx_get_hlid(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+ struct ieee80211_tx_info *control;
+
+ if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+ return cc33xx_tx_get_hlid_ap(cc, wlvif, skb, sta);
+
+ control = IEEE80211_SKB_CB(skb);
+ if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+ cc33xx_debug(DEBUG_TX, "tx offchannel");
+ return wlvif->dev_hlid;
+ }
+
+ return wlvif->sta.hlid;
+}
+
+unsigned int cc33xx_calc_packet_alignment(struct cc33xx *cc,
+ unsigned int packet_length)
+{
+ if ((cc->quirks & CC33XX_QUIRK_TX_PAD_LAST_FRAME) ||
+ !(cc->quirks & CC33XX_QUIRK_TX_BLOCKSIZE_ALIGN))
+ return ALIGN(packet_length, CC33XX_TX_ALIGN_TO);
+ else
+ return ALIGN(packet_length, CC33XX_BUS_BLOCK_SIZE);
+}
+EXPORT_SYMBOL(cc33xx_calc_packet_alignment);
+
+static u32 cc33xx_calc_tx_blocks(struct cc33xx *cc, u32 len, u32 spare_blks)
+{
+ u32 blk_size = CC33XX_TX_HW_BLOCK_SIZE;
+ /* In CC33xx the packet will be stored along with its internal descriptor.
+ * the descriptor is not part of the host transaction, but should be
+ * considered as part of the allocate memory blocks in the device
+ */
+ len = len + CC33xx_INTERNAL_DESC_SIZE;
+ return (len + blk_size - 1) / blk_size + spare_blks;
+}
+
+static inline void cc33xx_set_tx_desc_blocks(struct cc33xx *cc,
+ struct cc33xx_tx_hw_descr *desc,
+ u32 blks, u32 spare_blks)
+{
+ desc->cc33xx_mem.total_mem_blocks = blks;
+}
+
+static void cc33xx_set_tx_desc_data_len(struct cc33xx *cc,
+ struct cc33xx_tx_hw_descr *desc,
+ struct sk_buff *skb)
+{
+ desc->length = cpu_to_le16(skb->len);
+
+ /* if only the last frame is to be padded, we unset this bit on Tx */
+ if (cc->quirks & CC33XX_QUIRK_TX_PAD_LAST_FRAME)
+ desc->cc33xx_mem.ctrl = CC33XX_TX_CTRL_NOT_PADDED;
+ else
+ desc->cc33xx_mem.ctrl = 0;
+
+ cc33xx_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d",
+ desc->hlid, le16_to_cpu(desc->length),
+ le16_to_cpu(desc->life_time),
+ desc->cc33xx_mem.total_mem_blocks);
+}
+
+static int cc33xx_get_spare_blocks(struct cc33xx *cc, bool is_gem)
+{
+ /* If we have keys requiring extra spare, indulge them */
+ if (cc->extra_spare_key_count)
+ return CC33XX_TX_HW_EXTRA_BLOCK_SPARE;
+
+ return CC33XX_TX_HW_BLOCK_SPARE;
+}
+
+int cc33xx_tx_get_queue(int queue)
+{
+ switch (queue) {
+ case 0:
+ return CONF_TX_AC_VO;
+ case 1:
+ return CONF_TX_AC_VI;
+ case 2:
+ return CONF_TX_AC_BE;
+ case 3:
+ return CONF_TX_AC_BK;
+ default:
+ return CONF_TX_AC_BE;
+ }
+}
+
+static int cc33xx_tx_allocate(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, u32 extra, u32 buf_offset,
+ u8 hlid, bool is_gem,
+ struct NAB_tx_header *nab_cmd)
+{
+ struct cc33xx_tx_hw_descr *desc;
+ u32 total_blocks;
+ int id, ret = -EBUSY, ac;
+ u32 spare_blocks;
+ u32 total_skb_len = skb->len + extra;
+ /* Add NAB command required for CC33xx architecture */
+ u32 total_len = sizeof(struct NAB_tx_header);
+
+ total_skb_len += sizeof(struct cc33xx_tx_hw_descr);
+ total_len += total_skb_len;
+
+ cc33xx_debug(DEBUG_TX, "cc->tx_blocks_available %d",
+ cc->tx_blocks_available);
+
+ if (buf_offset + total_len > cc->aggr_buf_size)
+ return -EAGAIN;
+
+ spare_blocks = cc33xx_get_spare_blocks(cc, is_gem);
+
+ /* allocate free identifier for the packet */
+ id = cc33xx_alloc_tx_id(cc, skb);
+ if (id < 0)
+ return id;
+
+ /* memblocks should not include nab descriptor */
+ total_blocks = cc33xx_calc_tx_blocks(cc, total_skb_len, spare_blocks);
+ cc33xx_debug(DEBUG_TX, "total blocks %d", total_blocks);
+
+ if (total_blocks <= cc->tx_blocks_available) {
+ /**
+ * In CC33XX the packet starts with NAB command,
+ * only then the descriptor.
+ */
+ nab_cmd->sync = cpu_to_le32(HOST_SYNC_PATTERN);
+ nab_cmd->opcode = cpu_to_le16(NAB_SEND_CMD);
+
+ /**
+ * length should include the following 4 bytes
+ * of the NAB command.
+ */
+ nab_cmd->len = cpu_to_le16(total_len -
+ sizeof(struct NAB_header));
+ nab_cmd->desc_length = cpu_to_le16(total_len -
+ sizeof(struct NAB_tx_header));
+ nab_cmd->sd = 0;
+ nab_cmd->flags = NAB_SEND_FLAGS;
+
+ desc = skb_push(skb, total_skb_len - skb->len);
+
+ cc33xx_set_tx_desc_blocks(cc, desc, total_blocks, spare_blocks);
+
+ desc->id = id;
+
+ cc33xx_debug(DEBUG_TX,
+ "tx allocate id %u skb 0x%p tx_memblocks %d",
+ id, skb, desc->cc33xx_mem.total_mem_blocks);
+
+ cc->tx_blocks_available -= total_blocks;
+ cc->tx_allocated_blocks += total_blocks;
+
+ /* If the FW was empty before, arm the Tx watchdog. Also do
+ * this on the first Tx after resume, as we always cancel the
+ * watchdog on suspend.
+ */
+ if (cc->tx_allocated_blocks == total_blocks ||
+ test_and_clear_bit(CC33XX_FLAG_REINIT_TX_WDOG, &cc->flags))
+ cc33xx_rearm_tx_watchdog_locked(cc);
+
+ ac = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+ desc->ac = ac;
+ cc->tx_allocated_pkts[ac]++;
+
+ if (test_bit(hlid, cc->links_map))
+ cc->links[hlid].allocated_pkts++;
+
+ ret = 0;
+
+ cc33xx_debug(DEBUG_TX,
+ "tx_allocate: size: %d, blocks: %d, id: %d",
+ total_len, total_blocks, id);
+ } else {
+ cc33xx_free_tx_id(cc, id);
+ }
+
+ return ret;
+}
+
+static void cc33xx_tx_fill_hdr(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, u32 extra,
+ struct ieee80211_tx_info *control, u8 hlid)
+{
+ struct cc33xx_tx_hw_descr *desc;
+ int ac, rate_idx;
+ u16 tx_attr = 0;
+ __le16 frame_control;
+ struct ieee80211_hdr *hdr;
+ u8 *frame_start;
+ bool is_dummy;
+
+ desc = (struct cc33xx_tx_hw_descr *)skb->data;
+
+ frame_start = (u8 *)(desc + 1);
+ hdr = (struct ieee80211_hdr *)(frame_start + extra);
+ frame_control = hdr->frame_control;
+
+ /* relocate space for security header */
+ if (extra) {
+ int hdrlen = ieee80211_hdrlen(frame_control);
+
+ memmove(frame_start, hdr, hdrlen);
+ skb_set_network_header(skb, skb_network_offset(skb) + extra);
+ }
+
+ is_dummy = cc33xx_is_dummy_packet(cc, skb);
+ if (is_dummy || !wlvif || wlvif->bss_type != BSS_TYPE_AP_BSS)
+ desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
+ else
+ desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);
+
+ /* queue */
+ ac = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+ desc->tid = skb->priority;
+
+ if (is_dummy) {
+ /* FW expects the dummy packet to have an invalid session id -
+ * any session id that is different than the one set in the join
+ */
+ tx_attr = (SESSION_COUNTER_INVALID <<
+ TX_HW_ATTR_OFST_SESSION_COUNTER) &
+ TX_HW_ATTR_SESSION_COUNTER;
+
+ tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
+ } else if (wlvif) {
+ u8 session_id = cc->session_ids[hlid];
+
+ if (cc->quirks & CC33XX_QUIRK_AP_ZERO_SESSION_ID &&
+ wlvif->bss_type == BSS_TYPE_AP_BSS)
+ session_id = 0;
+
+ /* configure the tx attributes */
+ tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER;
+ }
+
+ desc->hlid = hlid;
+ if (is_dummy || !wlvif) {
+ rate_idx = 0;
+ } else if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
+ /* if the packets are data packets
+ * send them with AP rate policies (EAPOLs are an exception),
+ * otherwise use default basic rates
+ */
+ if (skb->protocol == cpu_to_be16(ETH_P_PAE))
+ rate_idx = wlvif->sta.basic_rate_idx;
+ else if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+ rate_idx = wlvif->sta.p2p_rate_idx;
+ else if (ieee80211_is_data(frame_control))
+ rate_idx = wlvif->sta.ap_rate_idx;
+ else
+ rate_idx = wlvif->sta.basic_rate_idx;
+ } else {
+ if (hlid == wlvif->ap.global_hlid)
+ rate_idx = wlvif->ap.mgmt_rate_idx;
+ else if (hlid == wlvif->ap.bcast_hlid ||
+ skb->protocol == cpu_to_be16(ETH_P_PAE) ||
+ !ieee80211_is_data(frame_control))
+ /* send non-data, bcast and EAPOLs using the
+ * min basic rate
+ */
+ rate_idx = wlvif->ap.bcast_rate_idx;
+ else
+ rate_idx = wlvif->ap.ucast_rate_idx[ac];
+ }
+
+ tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
+
+ /* for WEP shared auth - no fw encryption is needed */
+ if (ieee80211_is_auth(frame_control) &&
+ ieee80211_has_protected(frame_control))
+ tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
+
+ /* send EAPOL frames as voice */
+ if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)
+ tx_attr |= TX_HW_ATTR_EAPOL_FRAME;
+
+ desc->tx_attr = cpu_to_le16(tx_attr);
+
+ cc33xx_set_tx_desc_data_len(cc, desc, skb);
+}
+
+/* caller must hold cc->mutex */
+static int cc33xx_prepare_tx_frame(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, u32 buf_offset, u8 hlid)
+{
+ struct ieee80211_tx_info *info;
+ u32 extra = 0;
+ int ret = 0;
+ u32 total_len;
+ bool is_dummy;
+ bool is_gem = false;
+ struct NAB_tx_header nab_cmd;
+
+ if (!skb) {
+ cc33xx_error("discarding null skb");
+ return -EINVAL;
+ }
+
+ if (hlid == CC33XX_INVALID_LINK_ID) {
+ cc33xx_error("invalid hlid. dropping skb 0x%p", skb);
+ return -EINVAL;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+
+ is_dummy = cc33xx_is_dummy_packet(cc, skb);
+
+ if ((cc->quirks & CC33XX_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
+ info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ extra = CC33XX_EXTRA_SPACE_TKIP;
+
+ if (info->control.hw_key) {
+ bool is_wep;
+ u8 idx = info->control.hw_key->hw_key_idx;
+ u32 cipher = info->control.hw_key->cipher;
+
+ is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104);
+
+ if (WARN_ON(is_wep && wlvif && wlvif->default_key != idx)) {
+ ret = cc33xx_set_default_wep_key(cc, wlvif, idx);
+ if (ret < 0)
+ return ret;
+ wlvif->default_key = idx;
+ }
+
+ is_gem = (cipher == CC33XX_CIPHER_SUITE_GEM);
+ }
+
+ /* Add 4 bytes gap, may be filled later on by the PMAC. */
+ extra += IEEE80211_HT_CTL_LEN;
+ ret = cc33xx_tx_allocate(cc, wlvif, skb, extra, buf_offset, hlid,
+ is_gem, &nab_cmd);
+ cc33xx_debug(DEBUG_TX, "cc33xx_tx_allocate %d", ret);
+
+ if (ret < 0)
+ return ret;
+
+ cc33xx_tx_fill_hdr(cc, wlvif, skb, extra, info, hlid);
+
+ cc33xx_debug(DEBUG_TX, "cc33xx_tx_fill_hdr ");
+
+ if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
+ cc33xx_tx_ap_update_inconnection_sta(cc, wlvif, skb);
+ cc33xx_tx_regulate_link(cc, wlvif, hlid);
+ }
+
+ /* The length of each packet is stored in terms of
+ * words. Thus, we must pad the skb data to make sure its
+ * length is aligned. The number of padding bytes is computed
+ * and set in cc33xx_tx_fill_hdr.
+ * In special cases, we want to align to a specific block size
+ * (eg. for wl128x with SDIO we align to 256).
+ */
+ total_len = cc33xx_calc_packet_alignment(cc, skb->len);
+ cc33xx_debug(DEBUG_TX, "cc33xx_calc_packet_alignment ");
+
+ memcpy(cc->aggr_buf + buf_offset,
+ &nab_cmd, sizeof(struct NAB_tx_header));
+ memcpy(cc->aggr_buf + buf_offset + sizeof(struct NAB_tx_header),
+ skb->data, skb->len);
+ memset(cc->aggr_buf + buf_offset + sizeof(struct NAB_tx_header)
+ + skb->len, 0, total_len - skb->len);
+
+ /* Revert side effects in the dummy packet skb, so it can be reused */
+ if (is_dummy)
+ skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr));
+
+ return (total_len + sizeof(struct NAB_tx_header));
+}
+
+u32 cc33xx_tx_enabled_rates_get(struct cc33xx *cc, u32 rate_set,
+ enum nl80211_band rate_band)
+{
+ struct ieee80211_supported_band *band;
+ u32 enabled_rates = 0;
+ int bit;
+
+ band = cc->hw->wiphy->bands[rate_band];
+ for (bit = 0; bit < band->n_bitrates; bit++) {
+ if (rate_set & 0x1)
+ enabled_rates |= band->bitrates[bit].hw_value;
+ rate_set >>= 1;
+ }
+
+ /* MCS rates indication are on bits 16 - 31 */
+ rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
+
+ for (bit = 0; bit < 16; bit++) {
+ if (rate_set & 0x1)
+ enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
+ rate_set >>= 1;
+ }
+
+ return enabled_rates;
+}
+
+static inline int cc33xx_tx_get_mac80211_queue(struct cc33xx_vif *wlvif,
+ int queue)
+{
+ int mac_queue = wlvif->hw_queue_base;
+
+ switch (queue) {
+ case CONF_TX_AC_VO:
+ return mac_queue + 0;
+ case CONF_TX_AC_VI:
+ return mac_queue + 1;
+ case CONF_TX_AC_BE:
+ return mac_queue + 2;
+ case CONF_TX_AC_BK:
+ return mac_queue + 3;
+ default:
+ return mac_queue + 2;
+ }
+}
+
+static void cc33xx_wake_queue(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 queue, enum cc33xx_queue_stop_reason reason)
+{
+ unsigned long flags;
+ int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue);
+
+ spin_lock_irqsave(&cc->cc_lock, flags);
+
+ /* queue should not be clear for this reason */
+ WARN_ON_ONCE(!test_and_clear_bit(reason, &cc->queue_stop_reasons[hwq]));
+
+ if (cc->queue_stop_reasons[hwq])
+ goto out;
+
+ ieee80211_wake_queue(cc->hw, hwq);
+
+out:
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+}
+
+void cc33xx_handle_tx_low_watermark(struct cc33xx *cc)
+{
+ int i;
+ struct cc33xx_vif *wlvif;
+
+ cc33xx_for_each_wlvif(cc, wlvif) {
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ if (cc33xx_is_queue_stopped_by_reason(cc, wlvif, i,
+ CC33XX_QUEUE_STOP_REASON_WATERMARK) &&
+ wlvif->tx_queue_count[i] <=
+ CC33XX_TX_QUEUE_LOW_WATERMARK)
+ /* firmware buffer has space, restart queues */
+ cc33xx_wake_queue(cc, wlvif, i,
+ CC33XX_QUEUE_STOP_REASON_WATERMARK);
+ }
+ }
+}
+
+static int cc33xx_select_ac(struct cc33xx *cc)
+{
+ int i, q = -1, ac;
+ u32 min_pkts = 0xffffffff;
+
+ /* Find a non-empty ac where:
+ * 1. There are packets to transmit
+ * 2. The FW has the least allocated blocks
+ *
+ * We prioritize the ACs according to VO>VI>BE>BK
+ */
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ ac = cc33xx_tx_get_queue(i);
+ if (cc->tx_queue_count[ac] &&
+ cc->tx_allocated_pkts[ac] < min_pkts) {
+ q = ac;
+ min_pkts = cc->tx_allocated_pkts[q];
+ }
+ }
+
+ return q;
+}
+
+static struct sk_buff *cc33xx_lnk_dequeue(struct cc33xx *cc,
+ struct cc33xx_link *lnk, u8 q)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ skb = skb_dequeue(&lnk->tx_queue[q]);
+ if (skb) {
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ WARN_ON_ONCE(cc->tx_queue_count[q] <= 0);
+ cc->tx_queue_count[q]--;
+ if (lnk->wlvif) {
+ WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
+ lnk->wlvif->tx_queue_count[q]--;
+ }
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+ }
+
+ return skb;
+}
+
+static bool cc33xx_lnk_high_prio(struct cc33xx *cc, u8 hlid,
+ struct cc33xx_link *lnk)
+{
+ u8 thold;
+ struct core_fw_status *core_fw_status = &cc->core_status->fw_info;
+ unsigned long suspend_bitmap, fast_bitmap, ps_bitmap;
+
+ suspend_bitmap = le32_to_cpu(core_fw_status->link_suspend_bitmap);
+ fast_bitmap = le32_to_cpu(core_fw_status->link_fast_bitmap);
+ ps_bitmap = le32_to_cpu(core_fw_status->link_ps_bitmap);
+
+ /* suspended links are never high priority */
+ if (test_bit(hlid, &suspend_bitmap))
+ return false;
+
+ /* the priority thresholds are taken from FW */
+ if (test_bit(hlid, &fast_bitmap) && !test_bit(hlid, &ps_bitmap))
+ thold = core_fw_status->tx_fast_link_prio_threshold;
+ else
+ thold = core_fw_status->tx_slow_link_prio_threshold;
+
+ return lnk->allocated_pkts < thold;
+}
+
+static bool cc33xx_lnk_low_prio(struct cc33xx *cc, u8 hlid,
+ struct cc33xx_link *lnk)
+{
+ u8 thold;
+ struct core_fw_status *core_fw_status = &cc->core_status->fw_info;
+ unsigned long suspend_bitmap, fast_bitmap, ps_bitmap;
+
+ suspend_bitmap = le32_to_cpu(core_fw_status->link_suspend_bitmap);
+ fast_bitmap = le32_to_cpu(core_fw_status->link_fast_bitmap);
+ ps_bitmap = le32_to_cpu(core_fw_status->link_ps_bitmap);
+
+ if (test_bit(hlid, &suspend_bitmap))
+ thold = core_fw_status->tx_suspend_threshold;
+ else if (test_bit(hlid, &fast_bitmap) && !test_bit(hlid, &ps_bitmap))
+ thold = core_fw_status->tx_fast_stop_threshold;
+ else
+ thold = core_fw_status->tx_slow_stop_threshold;
+
+ return lnk->allocated_pkts < thold;
+}
+
+static struct sk_buff *cc33xx_lnk_dequeue_high_prio(struct cc33xx *cc,
+ u8 hlid, u8 ac,
+ u8 *low_prio_hlid)
+{
+ struct cc33xx_link *lnk = &cc->links[hlid];
+
+ if (!cc33xx_lnk_high_prio(cc, hlid, lnk)) {
+ if (*low_prio_hlid == CC33XX_INVALID_LINK_ID &&
+ !skb_queue_empty(&lnk->tx_queue[ac]) &&
+ cc33xx_lnk_low_prio(cc, hlid, lnk))
+ /* we found the first non-empty low priority queue */
+ *low_prio_hlid = hlid;
+
+ return NULL;
+ }
+
+ return cc33xx_lnk_dequeue(cc, lnk, ac);
+}
+
+static struct sk_buff *cc33xx_vif_dequeue_high_prio(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif,
+ u8 ac, u8 *hlid,
+ u8 *low_prio_hlid)
+{
+ struct sk_buff *skb = NULL;
+ int i, h, start_hlid;
+
+ /* start from the link after the last one */
+ start_hlid = (wlvif->last_tx_hlid + 1) % CC33XX_MAX_LINKS;
+
+ /* dequeue according to AC, round robin on each link */
+ for (i = 0; i < CC33XX_MAX_LINKS; i++) {
+ h = (start_hlid + i) % CC33XX_MAX_LINKS;
+
+ /* only consider connected stations */
+ if (!test_bit(h, wlvif->links_map))
+ continue;
+
+ skb = cc33xx_lnk_dequeue_high_prio(cc, h, ac, low_prio_hlid);
+ if (!skb)
+ continue;
+
+ wlvif->last_tx_hlid = h;
+ break;
+ }
+
+ if (!skb)
+ wlvif->last_tx_hlid = 0;
+
+ *hlid = wlvif->last_tx_hlid;
+ return skb;
+}
+
+static struct sk_buff *cc33xx_skb_dequeue(struct cc33xx *cc, u8 *hlid)
+{
+ unsigned long flags;
+ struct cc33xx_vif *wlvif = cc->last_wlvif;
+ struct sk_buff *skb = NULL;
+ int ac;
+ u8 low_prio_hlid = CC33XX_INVALID_LINK_ID;
+
+ ac = cc33xx_select_ac(cc);
+ if (ac < 0)
+ goto out;
+
+ /* continue from last wlvif (round robin) */
+ if (wlvif) {
+ cc33xx_for_each_wlvif_continue(cc, wlvif) {
+ if (!wlvif->tx_queue_count[ac])
+ continue;
+
+ skb = cc33xx_vif_dequeue_high_prio(cc, wlvif, ac, hlid,
+ &low_prio_hlid);
+ if (!skb)
+ continue;
+
+ cc->last_wlvif = wlvif;
+ break;
+ }
+ }
+
+ /* dequeue from the system HLID before the restarting wlvif list */
+ if (!skb) {
+ skb = cc33xx_lnk_dequeue_high_prio(cc, CC33XX_SYSTEM_HLID,
+ ac, &low_prio_hlid);
+ if (skb) {
+ *hlid = CC33XX_SYSTEM_HLID;
+ cc->last_wlvif = NULL;
+ }
+ }
+
+ /* Do a new pass over the wlvif list. But no need to continue
+ * after last_wlvif. The previous pass should have found it.
+ */
+ if (!skb) {
+ cc33xx_for_each_wlvif(cc, wlvif) {
+ if (!wlvif->tx_queue_count[ac])
+ goto next;
+
+ skb = cc33xx_vif_dequeue_high_prio(cc, wlvif, ac, hlid,
+ &low_prio_hlid);
+ if (skb) {
+ cc->last_wlvif = wlvif;
+ break;
+ }
+
+next:
+ if (wlvif == cc->last_wlvif)
+ break;
+ }
+ }
+
+ /* no high priority skbs found - but maybe a low priority one? */
+ if (!skb && low_prio_hlid != CC33XX_INVALID_LINK_ID) {
+ struct cc33xx_link *lnk = &cc->links[low_prio_hlid];
+
+ skb = cc33xx_lnk_dequeue(cc, lnk, ac);
+
+ WARN_ON(!skb); /* we checked this before */
+ *hlid = low_prio_hlid;
+
+ /* ensure proper round robin in the vif/link levels */
+ cc->last_wlvif = lnk->wlvif;
+ if (lnk->wlvif)
+ lnk->wlvif->last_tx_hlid = low_prio_hlid;
+ }
+
+out:
+ if (!skb &&
+ test_and_clear_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &cc->flags)) {
+ int q;
+
+ skb = cc->dummy_packet;
+ *hlid = CC33XX_SYSTEM_HLID;
+ q = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ WARN_ON_ONCE(cc->tx_queue_count[q] <= 0);
+ cc->tx_queue_count[q]--;
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+ }
+
+ return skb;
+}
+
+static void cc33xx_skb_queue_head(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, u8 hlid)
+{
+ unsigned long flags;
+ int q = cc33xx_tx_get_queue(skb_get_queue_mapping(skb));
+
+ if (cc33xx_is_dummy_packet(cc, skb)) {
+ set_bit(CC33XX_FLAG_DUMMY_PACKET_PENDING, &cc->flags);
+ } else {
+ skb_queue_head(&cc->links[hlid].tx_queue[q], skb);
+
+ /* make sure we dequeue the same packet next time */
+ wlvif->last_tx_hlid = (hlid + CC33XX_MAX_LINKS - 1) %
+ CC33XX_MAX_LINKS;
+ }
+
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ cc->tx_queue_count[q]++;
+ if (wlvif)
+ wlvif->tx_queue_count[q]++;
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+}
+
+static inline bool cc33xx_tx_is_data_present(struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);
+
+ return ieee80211_is_data_present(hdr->frame_control);
+}
+
+/* Returns failure values only in case of failed bus ops within this function.
+ * cc33xx_prepare_tx_frame retvals won't be returned in order to avoid
+ * triggering recovery by higher layers when not necessary.
+ * In case a FW command fails within cc33xx_prepare_tx_frame fails a recovery
+ * will be queued in cc33xx_cmd_send. -EAGAIN/-EBUSY from prepare_tx_frame
+ * can occur and are legitimate so don't propagate. -EINVAL will emit a WARNING
+ * within prepare_tx_frame code but there's nothing we should do about those
+ * as well.
+ */
+int cc33xx_tx_work_locked(struct cc33xx *cc)
+{
+ struct cc33xx_vif *wlvif;
+ struct sk_buff *skb;
+ struct cc33xx_tx_hw_descr *desc;
+ u32 buf_offset = 0, last_len = 0;
+ u32 transfer_len = 0;
+ u32 padding_size = 0;
+ bool sent_packets = false;
+ unsigned long active_hlids[BITS_TO_LONGS(CC33XX_MAX_LINKS)] = {0};
+ int ret = 0;
+ int bus_ret = 0;
+ u8 hlid;
+
+ cc33xx_debug(DEBUG_TX, " Tx work locked");
+
+ memset(cc->aggr_buf, 0, 0x300);
+ if (unlikely(cc->state != CC33XX_STATE_ON))
+ return 0;
+
+ while ((skb = cc33xx_skb_dequeue(cc, &hlid))) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ bool has_data = false;
+
+ cc33xx_debug(DEBUG_TX, "skb dequeue skb: 0x%p data %#lx head %#lx tail %#lx end %#lx",
+ skb,
+ (unsigned long)skb->data, (unsigned long)skb->head,
+ (unsigned long)skb->tail, (unsigned long)skb->end);
+ wlvif = NULL;
+ if (!cc33xx_is_dummy_packet(cc, skb))
+ wlvif = cc33xx_vif_to_data(info->control.vif);
+ else
+ hlid = CC33XX_SYSTEM_HLID;
+
+ has_data = wlvif && cc33xx_tx_is_data_present(skb);
+ ret = cc33xx_prepare_tx_frame(cc, wlvif, skb, buf_offset,
+ hlid);
+
+ if (ret == -EAGAIN) {
+ /* Aggregation buffer is full.
+ * Flush buffer and try again.
+ */
+ cc33xx_skb_queue_head(cc, wlvif, skb, hlid);
+
+ transfer_len = __ALIGN_MASK(buf_offset,
+ CC33XX_BUS_BLOCK_SIZE * 2 - 1);
+
+ padding_size = transfer_len - buf_offset;
+ memset(cc->aggr_buf + buf_offset, 0x33, padding_size);
+
+ cc33xx_debug(DEBUG_TX, "sdio transaction length: %d ",
+ transfer_len);
+
+ bus_ret = cc33xx_write(cc, NAB_DATA_ADDR, cc->aggr_buf,
+ transfer_len, true);
+ if (bus_ret < 0)
+ goto out;
+
+ sent_packets = true;
+ buf_offset = 0;
+ continue;
+ } else if (ret == -EBUSY) {
+ /* Firmware buffer is full.
+ * Queue back last skb, and stop aggregating.
+ */
+ cc33xx_skb_queue_head(cc, wlvif, skb, hlid);
+ /* No work left, avoid scheduling redundant tx work */
+ set_bit(CC33XX_FLAG_FW_TX_BUSY, &cc->flags);
+ goto out_ack;
+ } else if (ret < 0) {
+ if (cc33xx_is_dummy_packet(cc, skb))
+ /* fw still expects dummy packet,
+ * so re-enqueue it
+ */
+ cc33xx_skb_queue_head(cc, wlvif, skb, hlid);
+ else
+ ieee80211_free_txskb(cc->hw, skb);
+ goto out_ack;
+ }
+
+ last_len = ret;
+ buf_offset += last_len;
+
+ if (has_data) {
+ desc = (struct cc33xx_tx_hw_descr *)skb->data;
+ __set_bit(desc->hlid, active_hlids);
+ }
+ }
+
+out_ack:
+ if (buf_offset) {
+ transfer_len = __ALIGN_MASK(buf_offset,
+ CC33XX_BUS_BLOCK_SIZE * 2 - 1);
+
+ padding_size = transfer_len - buf_offset;
+ memset(cc->aggr_buf + buf_offset, 0x33, padding_size);
+
+ cc33xx_debug(DEBUG_TX, "sdio transaction (926) length: %d ",
+ transfer_len);
+
+ bus_ret = cc33xx_write(cc, NAB_DATA_ADDR, cc->aggr_buf,
+ transfer_len, true);
+ if (bus_ret < 0)
+ goto out;
+
+ sent_packets = true;
+ }
+
+ if (sent_packets)
+ cc33xx_handle_tx_low_watermark(cc);
+
+out:
+ return bus_ret;
+}
+
+void cc33xx_tx_work(struct work_struct *work)
+{
+ struct cc33xx *cc = container_of(work, struct cc33xx, tx_work);
+ int ret;
+
+ mutex_lock(&cc->mutex);
+
+ ret = cc33xx_tx_work_locked(cc);
+ if (ret < 0) {
+ cc33xx_queue_recovery_work(cc);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&cc->mutex);
+}
+
+void cc33xx_tx_reset_link_queues(struct cc33xx *cc, u8 hlid)
+{
+ struct sk_buff *skb;
+ int i;
+ unsigned long flags;
+ struct ieee80211_tx_info *info;
+ int total[NUM_TX_QUEUES];
+ struct cc33xx_link *lnk = &cc->links[hlid];
+
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ total[i] = 0;
+ while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
+ cc33xx_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
+
+ if (!cc33xx_is_dummy_packet(cc, skb)) {
+ info = IEEE80211_SKB_CB(skb);
+ info->status.rates[0].idx = -1;
+ info->status.rates[0].count = 0;
+ ieee80211_tx_status_ni(cc->hw, skb);
+ }
+
+ total[i]++;
+ }
+ }
+
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ cc->tx_queue_count[i] -= total[i];
+ if (lnk->wlvif)
+ lnk->wlvif->tx_queue_count[i] -= total[i];
+ }
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+
+ cc33xx_handle_tx_low_watermark(cc);
+}
+
+/* caller must hold cc->mutex and TX must be stopped */
+void cc33xx_tx_reset_wlvif(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ int i;
+
+ /* TX failure */
+ for_each_set_bit(i, wlvif->links_map, CC33XX_MAX_LINKS) {
+ if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+ i != wlvif->ap.bcast_hlid &&
+ i != wlvif->ap.global_hlid) {
+ /* this calls cc33xx_clear_link */
+ cc33xx_free_sta(cc, wlvif, i);
+ } else {
+ u8 hlid = i;
+
+ cc33xx_clear_link(cc, wlvif, &hlid);
+ }
+ }
+
+ wlvif->last_tx_hlid = 0;
+
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ wlvif->tx_queue_count[i] = 0;
+}
+
+int cc33xx_tx_total_queue_count(struct cc33xx *cc)
+{
+ int i, count = 0;
+
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ count += cc->tx_queue_count[i];
+
+ return count;
+}
+
+/* caller must hold cc->mutex and TX must be stopped */
+void cc33xx_tx_reset(struct cc33xx *cc)
+{
+ int i;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+
+ /* only reset the queues if something bad happened */
+ if (cc33xx_tx_total_queue_count(cc) != 0) {
+ for (i = 0; i < CC33XX_MAX_LINKS; i++)
+ cc33xx_tx_reset_link_queues(cc, i);
+
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ cc->tx_queue_count[i] = 0;
+ }
+
+ /* Make sure the driver is at a consistent state, in case this
+ * function is called from a context other than interface removal.
+ * This call will always wake the TX queues.
+ */
+ cc33xx_handle_tx_low_watermark(cc);
+
+ for (i = 0; i < CC33XX_NUM_TX_DESCRIPTORS; i++) {
+ if (!cc->tx_frames[i])
+ continue;
+
+ skb = cc->tx_frames[i];
+ cc33xx_free_tx_id(cc, i);
+ cc33xx_debug(DEBUG_TX, "freeing skb 0x%p", skb);
+
+ if (!cc33xx_is_dummy_packet(cc, skb)) {
+ /* Remove private headers before passing the skb to
+ * mac80211
+ */
+ info = IEEE80211_SKB_CB(skb);
+ skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr));
+ if ((cc->quirks & CC33XX_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
+ info->control.hw_key->cipher ==
+ WLAN_CIPHER_SUITE_TKIP) {
+ int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+
+ memmove(skb->data + CC33XX_EXTRA_SPACE_TKIP,
+ skb->data, hdrlen);
+ skb_pull(skb, CC33XX_EXTRA_SPACE_TKIP);
+ }
+
+ info->status.rates[0].idx = -1;
+ info->status.rates[0].count = 0;
+
+ ieee80211_tx_status_ni(cc->hw, skb);
+ }
+ }
+}
+
+#define CC33XX_TX_FLUSH_TIMEOUT 500000
+
+/* caller must *NOT* hold cc->mutex */
+void cc33xx_tx_flush(struct cc33xx *cc)
+{
+ unsigned long timeout, start_time;
+ int i;
+
+ start_time = jiffies;
+ timeout = start_time + usecs_to_jiffies(CC33XX_TX_FLUSH_TIMEOUT);
+
+ /* only one flush should be in progress, for consistent queue state */
+ mutex_lock(&cc->flush_mutex);
+
+ mutex_lock(&cc->mutex);
+ if (cc->tx_frames_cnt == 0 && cc33xx_tx_total_queue_count(cc) == 0) {
+ mutex_unlock(&cc->mutex);
+ goto out;
+ }
+
+ cc33xx_stop_queues(cc, CC33XX_QUEUE_STOP_REASON_FLUSH);
+
+ while (!time_after(jiffies, timeout)) {
+ cc33xx_debug(DEBUG_MAC80211, "flushing tx buffer: %d %d",
+ cc->tx_frames_cnt,
+ cc33xx_tx_total_queue_count(cc));
+
+ /* force Tx and give the driver some time to flush data */
+ mutex_unlock(&cc->mutex);
+ if (cc33xx_tx_total_queue_count(cc))
+ cc33xx_tx_work(&cc->tx_work);
+ msleep(20);
+ mutex_lock(&cc->mutex);
+
+ if (cc->tx_frames_cnt == 0 && cc33xx_tx_total_queue_count(cc) == 0) {
+ cc33xx_debug(DEBUG_MAC80211, "tx flush took %d ms",
+ jiffies_to_msecs(jiffies - start_time));
+ goto out_wake;
+ }
+ }
+
+ cc33xx_warning("Unable to flush all TX buffers, timed out (timeout %d ms",
+ CC33XX_TX_FLUSH_TIMEOUT / 1000);
+
+ /* forcibly flush all Tx buffers on our queues */
+ for (i = 0; i < CC33XX_MAX_LINKS; i++)
+ cc33xx_tx_reset_link_queues(cc, i);
+
+out_wake:
+ cc33xx_wake_queues(cc, CC33XX_QUEUE_STOP_REASON_FLUSH);
+ mutex_unlock(&cc->mutex);
+out:
+ mutex_unlock(&cc->flush_mutex);
+}
+
+u32 cc33xx_tx_min_rate_get(struct cc33xx *cc, u32 rate_set)
+{
+ if (WARN_ON(!rate_set))
+ return 0;
+
+ return BIT(__ffs(rate_set));
+}
+
+void cc33xx_stop_queue_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 queue, enum cc33xx_queue_stop_reason reason)
+{
+ int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue);
+ bool stopped = !!cc->queue_stop_reasons[hwq];
+
+ /* queue should not be stopped for this reason */
+ WARN_ON_ONCE(test_and_set_bit(reason, &cc->queue_stop_reasons[hwq]));
+
+ if (stopped)
+ return;
+
+ ieee80211_stop_queue(cc->hw, hwq);
+}
+
+void cc33xx_stop_queues(struct cc33xx *cc,
+ enum cc33xx_queue_stop_reason reason)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cc->cc_lock, flags);
+
+ /* mark all possible queues as stopped */
+ for (i = 0; i < CC33XX_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) {
+ WARN_ON_ONCE(test_and_set_bit(reason,
+ &cc->queue_stop_reasons[i]));
+ }
+
+ /* use the global version to make sure all vifs in mac80211 we don't
+ * know are stopped.
+ */
+ ieee80211_stop_queues(cc->hw);
+
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+}
+
+void cc33xx_wake_queues(struct cc33xx *cc,
+ enum cc33xx_queue_stop_reason reason)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cc->cc_lock, flags);
+
+ /* mark all possible queues as awake */
+ for (i = 0; i < CC33XX_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) {
+ WARN_ON_ONCE(!test_and_clear_bit(reason,
+ &cc->queue_stop_reasons[i]));
+ }
+
+ /* use the global version to make sure all vifs in mac80211 we don't
+ * know are woken up.
+ */
+ ieee80211_wake_queues(cc->hw);
+
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+}
+
+bool cc33xx_is_queue_stopped_by_reason(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 queue,
+ enum cc33xx_queue_stop_reason reason)
+{
+ unsigned long flags;
+ bool stopped;
+
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ stopped = cc33xx_is_queue_stopped_by_reason_locked(cc, wlvif, queue,
+ reason);
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+
+ return stopped;
+}
+
+bool cc33xx_is_queue_stopped_by_reason_locked(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 queue,
+ enum cc33xx_queue_stop_reason reason)
+{
+ int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue);
+
+ assert_spin_locked(&cc->cc_lock);
+ return test_bit(reason, &cc->queue_stop_reasons[hwq]);
+}
+
+bool cc33xx_is_queue_stopped_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 queue)
+{
+ int hwq = cc33xx_tx_get_mac80211_queue(wlvif, queue);
+
+ assert_spin_locked(&cc->cc_lock);
+ return !!cc->queue_stop_reasons[hwq];
+}
+
+static void cc33xx_tx_complete_packet(struct cc33xx *cc, u8 tx_stat_byte,
+ struct core_fw_status *core_fw_status)
+{
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb;
+ int id = tx_stat_byte & CC33XX_TX_STATUS_DESC_ID_MASK;
+ bool tx_success;
+ struct cc33xx_tx_hw_descr *tx_desc;
+ u16 desc_session_idx;
+
+ /* check for id legality */
+ if (unlikely(id >= CC33XX_NUM_TX_DESCRIPTORS ||
+ !cc->tx_frames[id])) {
+ cc33xx_warning("illegal id in tx completion: %d", id);
+
+ print_hex_dump(KERN_DEBUG, "fw_info local:",
+ DUMP_PREFIX_OFFSET, 16, 4, (u8 *)(core_fw_status),
+ sizeof(struct core_fw_status), false);
+
+ cc33xx_queue_recovery_work(cc);
+ return;
+ }
+
+ /* a zero bit indicates Tx success */
+ tx_success = !(tx_stat_byte & BIT(CC33XX_TX_STATUS_STAT_BIT_IDX));
+
+ skb = cc->tx_frames[id];
+ info = IEEE80211_SKB_CB(skb);
+ tx_desc = (struct cc33xx_tx_hw_descr *)skb->data;
+
+ if (cc33xx_is_dummy_packet(cc, skb)) {
+ cc33xx_free_tx_id(cc, id);
+ return;
+ }
+
+ /* update the TX status info */
+ if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ info->status.rates[0].count = 1; /* no data about retries */
+ info->status.ack_signal = -1;
+
+ if (!tx_success)
+ cc->stats.retry_count++;
+
+ /* remove private header from packet */
+ skb_pull(skb, sizeof(struct cc33xx_tx_hw_descr));
+
+ /* remove TKIP header space if present */
+ if ((cc->quirks & CC33XX_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
+ info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+
+ memmove(skb->data + CC33XX_EXTRA_SPACE_TKIP, skb->data, hdrlen);
+ skb_pull(skb, CC33XX_EXTRA_SPACE_TKIP);
+ }
+
+ cc33xx_debug(DEBUG_TX,
+ "tx status id %u skb 0x%p success %d, tx_memblocks %d",
+ id, skb, tx_success, tx_desc->cc33xx_mem.total_mem_blocks);
+
+ /**
+ * in order to update the memory management
+ * we should have total_blocks, ac, and hlid
+ */
+ cc->tx_blocks_available += tx_desc->cc33xx_mem.total_mem_blocks;
+ cc->tx_allocated_blocks -= tx_desc->cc33xx_mem.total_mem_blocks;
+ /* per queue */
+
+ /* prevent wrap-around in freed-packets counter */
+ cc->tx_allocated_pkts[tx_desc->ac]--;
+
+ /* per link */
+ desc_session_idx = (le16_to_cpu(tx_desc->tx_attr) & TX_HW_ATTR_SESSION_COUNTER) >>
+ TX_HW_ATTR_OFST_SESSION_COUNTER;
+
+ if (cc->session_ids[tx_desc->hlid] == desc_session_idx)
+ cc->links[tx_desc->hlid].allocated_pkts--;
+
+ cc33xx_free_tx_id(cc, id);
+
+ /* new mem blocks are available now */
+ clear_bit(CC33XX_FLAG_FW_TX_BUSY, &cc->flags);
+
+ /* return the packet to the stack */
+ skb_queue_tail(&cc->deferred_tx_queue, skb);
+ queue_work(cc->freezable_wq, &cc->netstack_work);
+}
+
+void cc33xx_tx_immediate_complete(struct cc33xx *cc)
+{
+ u8 tx_result_queue_index;
+ struct core_fw_status core_fw_status;
+ u8 i;
+
+ claim_core_status_lock(cc);
+ memcpy(&core_fw_status, &cc->core_status->fw_info,
+ sizeof(struct core_fw_status));
+
+ tx_result_queue_index = cc->core_status->fw_info.tx_result_queue_index;
+ /* Lock guarantees we shadow tx_result_queue_index NOT during
+ * an active transaction. Subsequent references to fw_info can be done
+ * without locking as long we do not pass this index.
+ */
+ release_core_status_lock(cc);
+
+ cc33xx_debug(DEBUG_TX, "last released desc = %d, current idx = %d",
+ cc->last_fw_rls_idx, tx_result_queue_index);
+
+ /* nothing to do here */
+ if (cc->last_fw_rls_idx == tx_result_queue_index)
+ return;
+
+ /* freed Tx descriptors */
+
+ if (tx_result_queue_index >= TX_RESULT_QUEUE_SIZE) {
+ cc33xx_error("invalid desc release index %d",
+ tx_result_queue_index);
+ WARN_ON(1);
+ return;
+ }
+
+ cc33xx_debug(DEBUG_TX, "TX result queue! priv last fw idx %d, current resut index %d ",
+ cc->last_fw_rls_idx, tx_result_queue_index);
+
+ for (i = cc->last_fw_rls_idx; i != tx_result_queue_index;
+ i = (i + 1) % TX_RESULT_QUEUE_SIZE) {
+ cc33xx_tx_complete_packet(cc, core_fw_status.tx_result_queue[i],
+ &core_fw_status);
+ }
+
+ cc->last_fw_rls_idx = tx_result_queue_index;
+}
diff --git a/drivers/net/wireless/ti/cc33xx/tx.h b/drivers/net/wireless/ti/cc33xx/tx.h
new file mode 100644
index 000000000000..9062d50c7c8e
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/tx.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __TX_H__
+#define __TX_H__
+
+#define CC33XX_TX_HW_BLOCK_SPARE 1
+/* for special cases - namely, TKIP and GEM */
+#define CC33XX_TX_HW_EXTRA_BLOCK_SPARE 2
+#define CC33XX_TX_HW_BLOCK_SIZE 256
+
+#define CC33XX_TX_STATUS_DESC_ID_MASK 0x7F
+#define CC33XX_TX_STATUS_STAT_BIT_IDX 7
+
+/* Indicates this TX HW frame is not padded to SDIO block size */
+#define CC33XX_TX_CTRL_NOT_PADDED BIT(7)
+
+#define TX_HW_MGMT_PKT_LIFETIME_TU 2000
+#define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000
+
+#define TX_HW_ATTR_SESSION_COUNTER (BIT(2) | BIT(3) | BIT(4))
+#define TX_HW_ATTR_TX_DUMMY_REQ BIT(13)
+#define TX_HW_ATTR_HOST_ENCRYPT BIT(14)
+#define TX_HW_ATTR_EAPOL_FRAME BIT(15)
+
+#define TX_HW_ATTR_OFST_SESSION_COUNTER 2
+#define TX_HW_ATTR_OFST_RATE_POLICY 5
+
+#define CC33XX_TX_ALIGN_TO 4
+#define CC33XX_EXTRA_SPACE_TKIP 4
+#define CC33XX_EXTRA_SPACE_AES 8
+#define CC33XX_EXTRA_SPACE_MAX 8
+
+#define CC33XX_TX_EXTRA_HEADROOM \
+ (sizeof(struct cc33xx_tx_hw_descr) + IEEE80211_HT_CTL_LEN)
+
+/* Used for management frames and dummy packets */
+#define CC33XX_TID_MGMT 7
+
+/* stop a ROC for pending authentication reply after this time (ms) */
+#define CC33XX_PEND_AUTH_ROC_TIMEOUT 1000
+#define CC33xx_PEND_ROC_COMPLETE_TIMEOUT 2000
+
+struct cc33xx_tx_mem {
+ /* Total number of memory blocks allocated by the host for
+ * this packet.
+ */
+ u8 total_mem_blocks;
+
+ /* control bits
+ */
+ u8 ctrl;
+} __packed;
+
+/* On cc33xx based devices, when TX packets are aggregated, each packet
+ * size must be aligned to the SDIO block size. The maximum block size
+ * is bounded by the type of the padded bytes field that is sent to the
+ * FW. The HW maximum block size is 256 bytes. We use 128 to utilize the
+ * SDIO built-in busy signal when the FIFO is full.
+ */
+#define CC33XX_BUS_BLOCK_SIZE 128
+
+struct cc33xx_tx_hw_descr {
+ /* Length of packet in words, including descriptor+header+data */
+ __le16 length;
+
+ struct cc33xx_tx_mem cc33xx_mem;
+
+ /* Packet identifier used also in the Tx-Result. */
+ u8 id;
+ /* The packet TID value (as User-Priority) */
+ u8 tid;
+ /* host link ID (HLID) */
+ u8 hlid;
+ u8 ac;
+ /* Max delay in TUs until transmission. The last device time the
+ * packet can be transmitted is: start_time + (1024 * life_time)
+ */
+ __le16 life_time;
+ /* Bitwise fields - see TX_ATTR... definitions above. */
+ __le16 tx_attr;
+} __packed;
+
+struct cc33xx_tx_hw_res_descr {
+ /* Packet Identifier - same value used in the Tx descriptor.*/
+ u8 id;
+ /* The status of the transmission, indicating success or one of
+ * several possible reasons for failure.
+ */
+ u8 status;
+ /* Total air access duration including all retrys and overheads.*/
+ __le16 medium_usage;
+ /* The time passed from host xfer to Tx-complete.*/
+ __le32 fw_handling_time;
+ /* Total media delay
+ * (from 1st EDCA AIFS counter until TX Complete).
+ */
+ __le32 medium_delay;
+ /* LS-byte of last TKIP seq-num (saved per AC for recovery). */
+ u8 tx_security_sequence_number_lsb;
+ /* Retry count - number of transmissions without successful ACK.*/
+ u8 ack_failures;
+ /* The rate that succeeded getting ACK
+ * (Valid only if status=SUCCESS).
+ */
+ u8 rate_class_index;
+ /* for 4-byte alignment. */
+ u8 spare;
+} __packed;
+
+enum cc33xx_queue_stop_reason {
+ CC33XX_QUEUE_STOP_REASON_WATERMARK,
+ CC33XX_QUEUE_STOP_REASON_FW_RESTART,
+ CC33XX_QUEUE_STOP_REASON_FLUSH,
+ CC33XX_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */
+};
+
+int cc33xx_tx_get_queue(int queue);
+int cc33xx_tx_total_queue_count(struct cc33xx *cc);
+void cc33xx_tx_immediate_complete(struct cc33xx *cc);
+void cc33xx_tx_work(struct work_struct *work);
+int cc33xx_tx_work_locked(struct cc33xx *cc);
+void cc33xx_tx_reset_wlvif(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+void cc33xx_tx_reset(struct cc33xx *cc);
+void cc33xx_tx_flush(struct cc33xx *cc);
+u8 cc33xx_rate_to_idx(struct cc33xx *cc, u8 rate, enum nl80211_band band);
+u32 cc33xx_tx_enabled_rates_get(struct cc33xx *cc, u32 rate_set,
+ enum nl80211_band rate_band);
+u32 cc33xx_tx_min_rate_get(struct cc33xx *cc, u32 rate_set);
+u8 cc33xx_tx_get_hlid(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct sk_buff *skb, struct ieee80211_sta *sta);
+void cc33xx_tx_reset_link_queues(struct cc33xx *cc, u8 hlid);
+void cc33xx_handle_tx_low_watermark(struct cc33xx *cc);
+bool cc33xx_is_dummy_packet(struct cc33xx *cc, struct sk_buff *skb);
+unsigned int cc33xx_calc_packet_alignment(struct cc33xx *cc,
+ unsigned int packet_length);
+void cc33xx_free_tx_id(struct cc33xx *cc, int id);
+void cc33xx_stop_queue_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 queue, enum cc33xx_queue_stop_reason reason);
+void cc33xx_stop_queues(struct cc33xx *cc,
+ enum cc33xx_queue_stop_reason reason);
+void cc33xx_wake_queues(struct cc33xx *cc,
+ enum cc33xx_queue_stop_reason reason);
+bool cc33xx_is_queue_stopped_by_reason(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 queue,
+ enum cc33xx_queue_stop_reason reason);
+bool cc33xx_is_queue_stopped_by_reason_locked(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif,
+ u8 queue,
+ enum cc33xx_queue_stop_reason reason);
+bool cc33xx_is_queue_stopped_locked(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 queue);
+
+/* from main.c */
+void cc33xx_free_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 hlid);
+void cc33xx_rearm_tx_watchdog_locked(struct cc33xx *cc);
+
+#endif /* __TX_H__ */
--
2.25.1


2024-05-21 17:22:25

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 17/17] Add ti,cc33xx.yaml

From: Michael Nemanov <[email protected]>

Signed-off-by: Michael Nemanov <[email protected]>
---
.../bindings/net/wireless/ti,cc33xx.yaml | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/wireless/ti,cc33xx.yaml

diff --git a/Documentation/devicetree/bindings/net/wireless/ti,cc33xx.yaml b/Documentation/devicetree/bindings/net/wireless/ti,cc33xx.yaml
new file mode 100644
index 000000000000..342e4e59d4cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/wireless/ti,cc33xx.yaml
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/wireless/ti,cc33xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments CC33xx Wireless LAN Controller
+
+maintainers:
+ - Michael Nemanov <[email protected]>
+
+description:
+ These are dt entries for the IEEE 802.11ax chips CC33xx from Texas Instruments.
+ Currently, these chips must be connected via SDIO.
+
+properties:
+ compatible:
+ enum:
+ - ti,cc3300
+ - ti,cc3301
+ - ti,cc3350
+ - ti,cc3351
+
+ reg:
+ description:
+ For WLAN communication, <reg> must be set to 2.
+ maxItems: 1
+
+ interrupts:
+ description: The interrupt line. Can be IRQ_TYPE_EDGE_RISING or IRQ_TYPE_LEVEL_HIGH.
+ When SDIO is used, the "in-band" interrupt provided by the SDIO bus is used
+ unless an interrupt is defined in the Device Tree.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ // SDIO example:
+ mmc3 {
+ vmmc-supply = <&wlan_en_reg>;
+ bus-width = <4>;
+ cap-power-off-card;
+ keep-power-in-suspend;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cc33xx: cc33xx@0 {
+ compatible = "ti,cc3300";
+ reg = <2>;
+ interrupts = <19 IRQ_TYPE_EDGE_RISING>;
+ };
+ };
--
2.25.1


2024-05-21 17:22:32

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 04/17] Add cmd.c, cmd.h

From: Michael Nemanov <[email protected]>

This is the command infrastructure for the CC33xx.
Similar to wlcore, all commands eventually reach
__cc33xx_cmd_send which fills a generic command
header and send the buffer via the IO abstraction layer.
Unlike wlcore, there is no polling for command completion
which is received via an interrupt which signals
cc->command_complete.

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/cmd.c | 2033 ++++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/cmd.h | 700 +++++++++
2 files changed, 2733 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/cmd.c
create mode 100644 drivers/net/wireless/ti/cc33xx/cmd.h

diff --git a/drivers/net/wireless/ti/cc33xx/cmd.c b/drivers/net/wireless/ti/cc33xx/cmd.c
new file mode 100644
index 000000000000..481745e79044
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/cmd.c
@@ -0,0 +1,2033 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include "acx.h"
+#include "event.h"
+#include "io.h"
+#include "tx.h"
+
+#define CC33XX_REBOOT_TIMEOUT_MSEC 100
+
+static void init_cmd_header(struct cc33xx_cmd_header *header,
+ size_t cmd_len, u16 id)
+{
+ header->NAB_header.len = cpu_to_le16(cmd_len);
+ WARN_ON(le16_to_cpu(header->NAB_header.len) != cmd_len);
+
+ header->NAB_header.sync_pattern = cpu_to_le32(HOST_SYNC_PATTERN);
+ header->NAB_header.opcode = cpu_to_le16(id);
+}
+
+int cc33xx_set_max_buffer_size(struct cc33xx *cc, enum buffer_size max_buffer_size)
+{
+ switch (max_buffer_size) {
+ case INI_MAX_BUFFER_SIZE:
+ /* INI FILE PAYLOAD SIZE + INI CMD PARAM + INT */
+ cc->max_cmd_size = CC33XX_INI_CMD_MAX_SIZE;
+ cc->max_cmd_size += sizeof(struct cc33xx_cmd_ini_params_download);
+ cc->max_cmd_size += sizeof(u32);
+ break;
+
+ case CMD_MAX_BUFFER_SIZE:
+ cc->max_cmd_size = CC33XX_CMD_MAX_SIZE;
+ break;
+
+ default:
+ cc33xx_warning("max_buffer_size invalid, not changing buffer size");
+ break;
+ }
+
+ return 0;
+}
+
+static int send_buffer(struct cc33xx *cc, int cmd_box_addr,
+ void *buf, size_t len)
+{
+ size_t max_cmd_size_align;
+
+ memcpy(cc->cmd_buf, buf, len);
+
+ memset(cc->cmd_buf + len, 0, (CC33XX_CMD_BUFFER_SIZE) - len);
+
+ max_cmd_size_align = __ALIGN_MASK(cc->max_cmd_size,
+ CC33XX_BUS_BLOCK_SIZE * 2 - 1);
+
+ return cc33xx_write(cc, cmd_box_addr, cc->cmd_buf,
+ max_cmd_size_align, true);
+}
+
+/* send command to firmware
+ *
+ * @cc: cc struct
+ * @id: command id
+ * @buf: buffer containing the command, must work with dma
+ * @len: length of the buffer
+ * return the cmd status code on success.
+ */
+static int __cc33xx_cmd_send(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, size_t res_len, bool sync)
+{
+ struct cc33xx_cmd_header *cmd;
+ unsigned long timeout;
+ int ret;
+
+ if (id >= CMD_LAST_SUPPORTED_COMMAND) {
+ cc33xx_debug(DEBUG_CMD, "command ID: %d, blocked", id);
+ return CMD_STATUS_SUCCESS;
+ }
+
+ if (WARN_ON(len < sizeof(*cmd)) ||
+ WARN_ON(len > cc->max_cmd_size) ||
+ WARN_ON(len % 4 != 0))
+ return -EIO;
+
+ cmd = buf;
+ cmd->id = cpu_to_le16(id);
+ cmd->status = 0;
+
+ init_cmd_header(cmd, len, id);
+ init_completion(&cc->command_complete);
+ ret = send_buffer(cc, NAB_DATA_ADDR, buf, len);
+
+ if (ret < 0)
+ return ret;
+
+ if (unlikely(!sync))
+ return CMD_STATUS_SUCCESS;
+
+ timeout = msecs_to_jiffies(CC33XX_COMMAND_TIMEOUT);
+ ret = wait_for_completion_timeout(&cc->command_complete, timeout);
+
+ if (ret < 1) {
+ cc33xx_debug(DEBUG_CMD, "Command T.O");
+ return -EIO;
+ }
+
+ switch (id) {
+ case CMD_INTERROGATE:
+ case CMD_DEBUG_READ:
+ case CMD_TEST_MODE:
+ case CMD_BM_READ_DEVICE_INFO:
+ cc33xx_debug(DEBUG_CMD,
+ "Response len %d, allocated buffer len %zu",
+ cc->result_length, res_len);
+
+ if (!res_len)
+ break; /* Response should be discarded */
+
+ if (WARN_ON(cc->result_length > res_len)) {
+ cc33xx_error("Error, insufficient response buffer");
+ break;
+ }
+
+ memcpy(buf + sizeof(struct NAB_header),
+ cc->command_result,
+ cc->result_length);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return CMD_STATUS_SUCCESS;
+}
+
+/* send command to fw and return cmd status on success
+ * valid_rets contains a bitmap of allowed error codes
+ */
+static int cc33xx_cmd_send_failsafe(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, size_t res_len,
+ unsigned long valid_rets)
+{
+ int ret = __cc33xx_cmd_send(cc, id, buf, len, res_len, true);
+
+ cc33xx_debug(DEBUG_TESTMODE, "CMD# %d, len=%zu", id, len);
+
+ if (ret < 0)
+ goto fail;
+
+ /* success is always a valid status */
+ valid_rets |= BIT(CMD_STATUS_SUCCESS);
+
+ if (ret >= MAX_COMMAND_STATUS || !test_bit(ret, &valid_rets)) {
+ cc33xx_error("command execute failure %d", ret);
+ ret = -EIO;
+ }
+
+ return ret;
+fail:
+ cc33xx_queue_recovery_work(cc);
+ return ret;
+}
+
+/* wrapper for cc33xx_cmd_send that accept only CMD_STATUS_SUCCESS
+ * return 0 on success.
+ */
+int cc33xx_cmd_send(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, size_t res_len)
+{
+ int ret;
+ /* Support id */
+ switch ((enum cc33xx_cmd)id) {
+ case CMD_EMPTY:
+ case CMD_START_DHCP_MGMT_SEQ:
+ case CMD_STOP_DHCP_MGMT_SEQ:
+ case CMD_START_SECURITY_MGMT_SEQ:
+ case CMD_STOP_SECURITY_MGMT_SEQ:
+ case CMD_START_ARP_MGMT_SEQ:
+ case CMD_STOP_ARP_MGMT_SEQ:
+ case CMD_START_DNS_MGMT_SEQ:
+ case CMD_STOP_DNS_MGMT_SEQ:
+ case CMD_SEND_DEAUTH_DISASSOC:
+ case CMD_SCHED_STATE_EVENT:
+ {
+ return 0;
+ } break;
+ default:
+ {
+ if ((enum cc33xx_cmd)id >= CMD_LAST_SUPPORTED_COMMAND)
+ return 0;
+ goto send;
+ }
+ }
+send:
+ ret = cc33xx_cmd_send_failsafe(cc, id, buf, len, res_len, 0);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cc33xx_count_role_set_bits(unsigned long role_map)
+{
+ int count = 0;
+ /* if device bit is set ( BIT_2 = ROLE_DEVICE)
+ * since role device is not counted
+ * remove it from map
+ */
+ role_map &= ~BIT(2);
+
+ while (role_map != 0) {
+ count += role_map & 1;
+ role_map >>= 1;
+ }
+
+ return count;
+}
+
+int cc33xx_cmd_role_enable(struct cc33xx *cc, u8 *addr,
+ u8 role_type, u8 *role_id)
+{
+ struct cc33xx_cmd_role_enable *cmd;
+
+ int ret;
+ unsigned long role_count;
+
+ struct cc33xx_cmd_complete_role_enable *command_complete =
+ (struct cc33xx_cmd_complete_role_enable *)&cc->command_result;
+
+ role_count = *cc->roles_map;
+ ret = cc33xx_count_role_set_bits(role_count);
+ cc33xx_debug(DEBUG_CMD, "cmd roles enabled: bitmap before: %ld, ret=%d",
+ role_count, ret);
+
+ /* do not enable more than 2 roles at once, exception is device role */
+ if (ret >= 2 && role_type != CC33XX_ROLE_DEVICE) {
+ cc33xx_debug(DEBUG_CMD,
+ "cmd role enable: 2 roles already have beed allocated");
+ cc33xx_error("failed to initiate cmd role enable");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role enable, role type %d, addr = %pM",
+ role_type, addr);
+
+ if (WARN_ON(*role_id != CC33XX_INVALID_ROLE_ID))
+ return -EBUSY;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(cmd->mac_address, addr, ETH_ALEN);
+ cmd->role_type = role_type;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_ENABLE, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role enable");
+ goto out_free;
+ }
+ cc33xx_debug(DEBUG_CMD, "complete role_id = %d",
+ command_complete->role_id);
+ __set_bit(command_complete->role_id, cc->roles_map);
+ *role_id = command_complete->role_id;
+
+out_free:
+ kfree(cmd);
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_disable(struct cc33xx *cc, u8 *role_id)
+{
+ struct cc33xx_cmd_role_disable *cmd;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD, "cmd role disable");
+
+ if (WARN_ON(*role_id == CC33XX_INVALID_ROLE_ID))
+ return -ENOENT;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ cmd->role_id = *role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_DISABLE, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role disable");
+ goto out_free;
+ }
+
+ __clear_bit(*role_id, cc->roles_map);
+ *role_id = CC33XX_INVALID_ROLE_ID;
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_set_link(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 link)
+{
+ unsigned long flags;
+
+ /* these bits are used by op_tx */
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ __set_bit(link, cc->links_map);
+ __set_bit(link, wlvif->links_map);
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+ cc->links[link].wlvif = wlvif;
+
+ /* Take saved value for total freed packets from wlvif, in case this is
+ * recovery/resume
+ */
+ if (wlvif->bss_type != BSS_TYPE_AP_BSS)
+ cc->links[link].total_freed_pkts = wlvif->total_freed_pkts;
+
+ cc->active_link_count++;
+ return 0;
+}
+
+void cc33xx_clear_link(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 *hlid)
+{
+ unsigned long flags;
+
+ if (*hlid == CC33XX_INVALID_LINK_ID)
+ return;
+
+ /* these bits are used by op_tx */
+ spin_lock_irqsave(&cc->cc_lock, flags);
+ __clear_bit(*hlid, cc->links_map);
+ __clear_bit(*hlid, wlvif->links_map);
+ spin_unlock_irqrestore(&cc->cc_lock, flags);
+
+ cc->links[*hlid].prev_freed_pkts = 0;
+ cc->links[*hlid].ba_bitmap = 0;
+ eth_zero_addr(cc->links[*hlid].addr);
+
+ /* At this point op_tx() will not add more packets to the queues. We
+ * can purge them.
+ */
+ cc33xx_tx_reset_link_queues(cc, *hlid);
+ cc->links[*hlid].wlvif = NULL;
+
+ if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+ *hlid == wlvif->ap.bcast_hlid) {
+ u32 sqn_padding = CC33XX_TX_SQN_POST_RECOVERY_PADDING;
+ /* save the total freed packets in the wlvif, in case this is
+ * recovery or suspend
+ */
+ wlvif->total_freed_pkts = cc->links[*hlid].total_freed_pkts;
+
+ /* increment the initial seq number on recovery to account for
+ * transmitted packets that we haven't yet got in the FW status
+ */
+ if (wlvif->encryption_type == KEY_GEM)
+ sqn_padding = CC33XX_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
+ if (test_bit(CC33XX_FLAG_RECOVERY_IN_PROGRESS, &cc->flags))
+ wlvif->total_freed_pkts += sqn_padding;
+ }
+
+ cc->links[*hlid].total_freed_pkts = 0;
+
+ *hlid = CC33XX_INVALID_LINK_ID;
+ cc->active_link_count--;
+ WARN_ON_ONCE(cc->active_link_count < 0);
+}
+
+static u8 cc33xx_get_native_channel_type(u8 nl_channel_type)
+{
+ switch (nl_channel_type) {
+ case NL80211_CHAN_NO_HT:
+ return CC33XX_CHAN_NO_HT;
+ case NL80211_CHAN_HT20:
+ return CC33XX_CHAN_HT20;
+ case NL80211_CHAN_HT40MINUS:
+ return CC33XX_CHAN_HT40MINUS;
+ case NL80211_CHAN_HT40PLUS:
+ return CC33XX_CHAN_HT40PLUS;
+ default:
+ WARN_ON(1);
+ return CC33XX_CHAN_NO_HT;
+ }
+}
+
+static int cc33xx_cmd_role_start_dev(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum nl80211_band band, int channel)
+{
+ struct cc33xx_cmd_role_start *cmd;
+ int ret;
+
+ struct cc33xx_cmd_complete_role_start *command_complete =
+ (struct cc33xx_cmd_complete_role_start *)&cc->command_result;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
+
+ cmd->role_id = wlvif->dev_role_id;
+ cmd->role_type = CC33XX_ROLE_DEVICE;
+ if (band == NL80211_BAND_5GHZ)
+ cmd->band = CC33XX_BAND_5GHZ;
+ cmd->channel = channel;
+ cmd->channel_type = cc33xx_get_native_channel_type(wlvif->channel_type);
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role device start");
+ goto err_hlid;
+ }
+
+ wlvif->dev_hlid = command_complete->sta.hlid;
+ cc->links[wlvif->dev_hlid].allocated_pkts = 0;
+ cc->session_ids[wlvif->dev_hlid] = command_complete->sta.session;
+ cc33xx_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d ",
+ wlvif->dev_role_id, command_complete->sta.hlid,
+ command_complete->sta.session);
+ ret = cc33xx_set_link(cc, wlvif, wlvif->dev_hlid);
+ goto out_free;
+
+err_hlid:
+ /* clear links on error */
+ cc33xx_clear_link(cc, wlvif, &wlvif->dev_hlid);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_stop_transceiver(struct cc33xx *cc)
+{
+ struct cc33xx_cmd_role_stop *cmd;
+ int ret;
+
+ if (unlikely(cc->state != CC33XX_STATE_ON || !cc->plt)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ cc33xx_debug(DEBUG_CMD, "cmd role stop transceiver");
+
+ cmd->role_id = cc->plt_role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("transceiver - failed to initiate cmd role stop");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_plt_disable(struct cc33xx *cc)
+{
+ struct cc33xx_cmd_PLT_disable *cmd;
+ int ret;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = cc33xx_cmd_send(cc, CMD_PLT_DISABLE, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("transceiver: failed to disable Transceiver mode");
+ goto out_free;
+ } else {
+ cc33xx_debug(DEBUG_CMD, "Succeed to disable Transceiver mode");
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+static int cc333xx_cmd_role_stop_dev(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif)
+{
+ struct cc33xx_cmd_role_stop *cmd;
+ int ret;
+
+ if (WARN_ON(wlvif->dev_hlid == CC33XX_INVALID_LINK_ID))
+ return -EINVAL;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role stop dev");
+
+ cmd->role_id = wlvif->dev_role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role stop");
+ goto out_free;
+ }
+
+ cc33xx_clear_link(cc, wlvif, &wlvif->dev_hlid);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_plt_enable(struct cc33xx *cc, u8 role_id)
+{
+ struct cc33xx_cmd_PLT_enable *cmd;
+ s32 ret;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = cc33xx_cmd_send(cc, CMD_PLT_ENABLE, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("Failed to send CMD_PLT_ENABLE");
+ goto out_free;
+ }
+ cc33xx_debug(DEBUG_CMD, "Success to send CMD_PLT_ENABLE");
+
+out_free:
+ kfree(cmd);
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_start_transceiver(struct cc33xx *cc, u8 role_id)
+{
+ struct cc33xx_cmd_role_start *cmd;
+ s32 ret;
+ u8 role_type = ROLE_TRANSCEIVER;
+
+ /* Default values */
+ u8 band = NL80211_BAND_2GHZ;
+ u8 channel = 6;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->role_type = role_type;
+ cmd->role_id = role_id;
+ cmd->channel = channel;
+ cmd->band = band;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role start PLT");
+ goto out_free;
+ }
+
+ cc33xx_debug(DEBUG_CMD,
+ "cmd role start PLT. Role ID number: %u", role_id);
+
+out_free:
+ kfree(cmd);
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_start_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+ struct cc33xx_cmd_role_start *cmd;
+
+ u32 supported_rates;
+ int ret;
+
+ struct cc33xx_cmd_complete_role_start *command_complete =
+ (struct cc33xx_cmd_complete_role_start *)&cc->command_result;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role start sta %d", wlvif->role_id);
+
+ cmd->role_id = wlvif->role_id;
+ cmd->role_type = CC33XX_ROLE_STA;
+ cmd->channel = wlvif->channel;
+ if (wlvif->band == NL80211_BAND_5GHZ) {
+ cmd->band = CC33XX_BAND_5GHZ;
+ cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set
+ & ~CONF_TX_CCK_RATES);
+ } else {
+ cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+ }
+ cmd->sta.beacon_interval = cpu_to_le16(wlvif->beacon_int);
+ cmd->sta.ssid_type = CC33XX_SSID_TYPE_ANY;
+ cmd->sta.ssid_len = wlvif->ssid_len;
+ memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
+ memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
+
+ supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | wlvif->rate_set;
+ if (wlvif->band == NL80211_BAND_5GHZ)
+ supported_rates &= ~CONF_TX_CCK_RATES;
+
+ if (wlvif->p2p)
+ supported_rates &= ~CONF_TX_CCK_RATES;
+
+ cmd->sta.local_rates = cpu_to_le32(supported_rates);
+
+ cmd->channel_type = cc33xx_get_native_channel_type(wlvif->channel_type);
+
+ /* We don't have the correct remote rates in this stage. The
+ * rates will be reconfigured later, after association, if the
+ * firmware supports ACX_PEER_CAP. Otherwise, there's nothing
+ * we can do, so use all supported_rates here.
+ */
+ cmd->sta.remote_rates = cpu_to_le32(supported_rates);
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role start sta");
+ goto err_hlid;
+ }
+
+ wlvif->sta.role_chan_type = wlvif->channel_type;
+
+ wlvif->sta.hlid = command_complete->sta.hlid;
+ cc->links[wlvif->sta.hlid].allocated_pkts = 0;
+ cc->session_ids[wlvif->sta.hlid] = command_complete->sta.session;
+ cc33xx_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d basic_rate_set: 0x%x, remote_rates: 0x%x",
+ wlvif->role_id,
+ command_complete->sta.hlid, command_complete->sta.session,
+ wlvif->basic_rate_set, wlvif->rate_set);
+ ret = cc33xx_set_link(cc, wlvif, wlvif->sta.hlid);
+
+ goto out_free;
+
+err_hlid:
+ cc33xx_clear_link(cc, wlvif, &wlvif->sta.hlid);
+
+out_free:
+ kfree(cmd);
+out:
+ return ret;
+}
+
+/* use this function to stop ibss as well */
+int cc33xx_cmd_role_stop_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct cc33xx_cmd_role_stop *cmd;
+ int ret;
+
+ if (WARN_ON(wlvif->sta.hlid == CC33XX_INVALID_LINK_ID))
+ return -EINVAL;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role stop sta %d", wlvif->role_id);
+
+ cmd->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role stop sta");
+ goto out_free;
+ }
+
+ cc33xx_clear_link(cc, wlvif, &wlvif->sta.hlid);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_start_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct cc33xx_cmd_role_start *cmd;
+ struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ u32 supported_rates;
+ int ret;
+
+ struct cc33xx_cmd_complete_role_start *command_complete =
+ (struct cc33xx_cmd_complete_role_start *)&cc->command_result;
+
+ cc33xx_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
+ cc33xx_debug(DEBUG_CMD, "cmd role start ap basic rateset: 0x%x",
+ wlvif->basic_rate_set);
+
+ /* If MESH --> ssid_len is always 0 */
+ if (!ieee80211_vif_is_mesh(vif)) {
+ /* trying to use hidden SSID with an old hostapd version */
+ if (wlvif->ssid_len == 0 && !bss_conf->hidden_ssid) {
+ cc33xx_error("got a null SSID from beacon/bss");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->role_id = wlvif->role_id;
+ cmd->role_type = CC33XX_ROLE_AP;
+ cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+ cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
+ cmd->ap.dtim_interval = bss_conf->dtim_period;
+ cmd->ap.wmm = wlvif->wmm_enabled;
+ cmd->channel = wlvif->channel;
+ cmd->channel_type = cc33xx_get_native_channel_type(wlvif->channel_type);
+
+ supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES;
+ if (wlvif->p2p)
+ supported_rates &= ~CONF_TX_CCK_RATES;
+
+ cc33xx_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
+ supported_rates);
+
+ cmd->ap.local_rates = cpu_to_le32(supported_rates);
+
+ switch (wlvif->band) {
+ case NL80211_BAND_2GHZ:
+ cmd->band = CC33XX_BAND_2_4GHZ;
+ break;
+ case NL80211_BAND_5GHZ:
+ cmd->band = CC33XX_BAND_5GHZ;
+ break;
+ default:
+ cc33xx_warning("ap start - unknown band: %d", (int)wlvif->band);
+ cmd->band = CC33XX_BAND_2_4GHZ;
+ break;
+ }
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role start ap");
+ goto out_free_bcast;
+ }
+
+ wlvif->ap.global_hlid = command_complete->ap.global_hlid;
+ wlvif->ap.bcast_hlid = command_complete->ap.broadcast_hlid;
+ cc->session_ids[wlvif->ap.global_hlid] = command_complete->ap.global_session_id;
+ cc->session_ids[wlvif->ap.bcast_hlid] = command_complete->ap.bcast_session_id;
+
+ cc33xx_debug(DEBUG_CMD, "role start: roleid=%d, global_hlid=%d, broadcast_hlid=%d, global_session_id=%d, bcast_session_id=%d, basic_rate_set: 0x%x, remote_rates: 0x%x",
+ wlvif->role_id,
+ command_complete->ap.global_hlid,
+ command_complete->ap.broadcast_hlid,
+ command_complete->ap.global_session_id,
+ command_complete->ap.bcast_session_id,
+ wlvif->basic_rate_set, wlvif->rate_set);
+
+ ret = cc33xx_set_link(cc, wlvif, wlvif->ap.global_hlid);
+ ret = cc33xx_set_link(cc, wlvif, wlvif->ap.bcast_hlid);
+
+ goto out_free;
+
+out_free_bcast:
+ cc33xx_clear_link(cc, wlvif, &wlvif->ap.bcast_hlid);
+ cc33xx_clear_link(cc, wlvif, &wlvif->ap.global_hlid);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_stop_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct cc33xx_cmd_role_stop *cmd;
+ int ret;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role stop ap %d", wlvif->role_id);
+
+ cmd->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role stop ap");
+ goto out_free;
+ }
+
+ cc33xx_clear_link(cc, wlvif, &wlvif->ap.bcast_hlid);
+ cc33xx_clear_link(cc, wlvif, &wlvif->ap.global_hlid);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_role_start_ibss(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+ struct cc33xx_cmd_role_start *cmd;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ int ret;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role start ibss %d", wlvif->role_id);
+
+ cmd->role_id = wlvif->role_id;
+ cmd->role_type = CC33XX_ROLE_IBSS;
+ if (wlvif->band == NL80211_BAND_5GHZ)
+ cmd->band = CC33XX_BAND_5GHZ;
+ cmd->channel = wlvif->channel;
+ cmd->ibss.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+ cmd->ibss.beacon_interval = cpu_to_le16(wlvif->beacon_int);
+ cmd->ibss.dtim_interval = bss_conf->dtim_period;
+ cmd->ibss.ssid_type = CC33XX_SSID_TYPE_ANY;
+ cmd->ibss.ssid_len = wlvif->ssid_len;
+ memcpy(cmd->ibss.ssid, wlvif->ssid, wlvif->ssid_len);
+ memcpy(cmd->ibss.bssid, vif->bss_conf.bssid, ETH_ALEN);
+ cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+
+ if (wlvif->sta.hlid == CC33XX_INVALID_LINK_ID) {
+ ret = cc33xx_set_link(cc, wlvif, wlvif->sta.hlid);
+ if (ret)
+ goto out_free;
+ }
+ cmd->ibss.hlid = wlvif->sta.hlid;
+ cmd->ibss.remote_rates = cpu_to_le32(wlvif->rate_set);
+
+ ret = cc33xx_cmd_send(cc, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd role enable");
+ goto err_hlid;
+ }
+
+ goto out_free;
+
+err_hlid:
+ /* clear links on error. */
+ cc33xx_clear_link(cc, wlvif, &wlvif->sta.hlid);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+/* send test command to firmware
+ *
+ * @cc: cc struct
+ * @buf: buffer containing the command, with all headers, must work with dma
+ * @len: length of the buffer
+ * @answer: is answer needed
+ */
+int cc33xx_cmd_test(struct cc33xx *cc, void *buf, size_t buf_len, u8 answer)
+{
+ int ret;
+ size_t res_len = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd test");
+
+ if (answer)
+ res_len = buf_len;
+
+ ret = cc33xx_cmd_send(cc, CMD_TEST_MODE, buf, buf_len, res_len);
+
+ if (ret < 0) {
+ cc33xx_warning("TEST command failed");
+ return ret;
+ }
+
+ return ret;
+}
+
+/* read acx from firmware
+ *
+ * @cc: cc struct
+ * @id: acx id
+ * @buf: buffer for the response, including all headers, must work with dma
+ * @len: length of buf
+ */
+int cc33xx_cmd_interrogate(struct cc33xx *cc, u16 id, void *buf,
+ size_t cmd_len, size_t res_len)
+{
+ struct acx_header *acx = buf;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD, "cmd interrogate");
+
+ acx->id = cpu_to_le16(id);
+
+ /* response payload length, does not include any headers */
+ acx->len = cpu_to_le16(res_len - sizeof(*acx));
+
+ ret = cc33xx_cmd_send(cc, CMD_INTERROGATE, acx, cmd_len, res_len);
+ if (ret < 0)
+ cc33xx_error("INTERROGATE command failed");
+
+ return ret;
+}
+
+/* read debug acx from firmware
+ *
+ * @cc: cc struct
+ * @id: acx id
+ * @buf: buffer for the response, including all headers, must work with dma
+ * @len: length of buf
+ */
+int cc33xx_cmd_debug_inter(struct cc33xx *cc, u16 id, void *buf,
+ size_t cmd_len, size_t res_len)
+{
+ struct acx_header *acx = buf;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD, "cmd debug interrogate");
+
+ acx->id = cpu_to_le16(id);
+
+ /* response payload length, does not include any headers */
+ acx->len = cpu_to_le16(res_len - sizeof(*acx));
+
+ ret = cc33xx_cmd_send(cc, CMD_DEBUG_READ, acx, cmd_len, res_len);
+ if (ret < 0)
+ cc33xx_error("CMD_DEBUG_READ command failed");
+
+ return ret;
+}
+
+/* write acx value to firmware
+ *
+ * @cc: cc struct
+ * @id: acx id
+ * @buf: buffer containing acx, including all headers, must work with dma
+ * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
+ */
+int cc33xx_cmd_configure_failsafe(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, unsigned long valid_rets)
+{
+ struct acx_header *acx = buf;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD, "cmd configure (%d), TSFL %x",
+ id, cc->core_status->tsf);
+
+ if (WARN_ON_ONCE(len < sizeof(*acx)))
+ return -EIO;
+
+ acx->id = cpu_to_le16(id);
+
+ /* payload length, does not include any headers */
+ acx->len = cpu_to_le16(len - sizeof(*acx));
+
+ ret = cc33xx_cmd_send_failsafe(cc, CMD_CONFIGURE, acx, len, 0,
+ valid_rets);
+ if (ret < 0) {
+ cc33xx_warning("CONFIGURE command NOK");
+ return ret;
+ }
+
+ return ret;
+}
+
+/* wrapper for cc33xx_cmd_configure that accepts only success status.
+ * return 0 on success
+ */
+int cc33xx_cmd_configure(struct cc33xx *cc, u16 id, void *buf, size_t len)
+{
+ int ret = cc33xx_cmd_configure_failsafe(cc, id, buf, len, 0);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/* write acx value to firmware
+ *
+ * @cc: cc struct
+ * @id: acx id
+ * @buf: buffer containing debug, including all headers, must work with dma
+ * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
+ */
+static int cc33xx_cmd_debug_failsafe(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, unsigned long valid_rets)
+{
+ struct debug_header *acx = buf;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD, "cmd debug (%d)", id);
+
+ if (WARN_ON_ONCE(len < sizeof(*acx)))
+ return -EIO;
+
+ acx->id = cpu_to_le16(id);
+
+ /* payload length, does not include any headers */
+ acx->len = cpu_to_le16(len - sizeof(*acx));
+
+ ret = cc33xx_cmd_send_failsafe(cc, CMD_DEBUG, acx, len, 0,
+ valid_rets);
+ if (ret < 0) {
+ cc33xx_warning("CONFIGURE command NOK");
+ return ret;
+ }
+
+ return ret;
+}
+
+/* wrapper for cc33xx_cmd_debug that accepts only success status.
+ * return 0 on success
+ */
+int cc33xx_cmd_debug(struct cc33xx *cc, u16 id, void *buf, size_t len)
+{
+ int ret = cc33xx_cmd_debug_failsafe(cc, id, buf, len, 0);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+int cc33xx_cmd_ps_mode(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 ps_mode, u16 auto_ps_timeout)
+{
+ struct cc33xx_cmd_ps_params *ps_params = NULL;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd set ps mode");
+
+ ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL);
+ if (!ps_params) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ps_params->role_id = wlvif->role_id;
+ ps_params->ps_mode = ps_mode;
+ ps_params->auto_ps_timeout = cpu_to_le16(auto_ps_timeout);
+
+ ret = cc33xx_cmd_send(cc, CMD_SET_PS_MODE, ps_params,
+ sizeof(*ps_params), 0);
+ if (ret < 0) {
+ cc33xx_error("cmd set_ps_mode failed");
+ goto out;
+ }
+
+out:
+ kfree(ps_params);
+ return ret;
+}
+
+int cc33xx_cmd_set_default_wep_key(struct cc33xx *cc, u8 id, u8 hlid)
+{
+ struct cc33xx_cmd_set_keys *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->hlid = hlid;
+ cmd->key_id = id;
+ cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
+ cmd->key_action = cpu_to_le16(KEY_SET_ID);
+ cmd->key_type = KEY_WEP;
+
+ ret = cc33xx_cmd_send(cc, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_warning("cmd set_default_wep_key failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+
+ return ret;
+}
+
+int cc33xx_cmd_set_sta_key(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u16 action, u8 id, u8 key_type, u8 key_size,
+ const u8 *key, const u8 *addr, u32 tx_seq_32,
+ u16 tx_seq_16)
+{
+ struct cc33xx_cmd_set_keys *cmd;
+ int ret = 0;
+
+ /* hlid might have already been deleted */
+ if (wlvif->sta.hlid == CC33XX_INVALID_LINK_ID)
+ return 0;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->hlid = wlvif->sta.hlid;
+
+ if (key_type == KEY_WEP)
+ cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
+ else if (is_broadcast_ether_addr(addr))
+ cmd->lid_key_type = BROADCAST_LID_TYPE;
+ else
+ cmd->lid_key_type = UNICAST_LID_TYPE;
+
+ cmd->key_action = cpu_to_le16(action);
+ cmd->key_size = key_size;
+ cmd->key_type = key_type;
+
+ cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
+ cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
+
+ cmd->key_id = id;
+
+ if (key_type == KEY_TKIP) {
+ /* We get the key in the following form:
+ * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
+ * but the target is expecting:
+ * TKIP - RX MIC - TX MIC
+ */
+ memcpy(cmd->key, key, 16);
+ memcpy(cmd->key + 16, key + 24, 8);
+ memcpy(cmd->key + 24, key + 16, 8);
+
+ } else {
+ memcpy(cmd->key, key, key_size);
+ }
+
+ cc33xx_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd));
+
+ ret = cc33xx_cmd_send(cc, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_warning("could not set keys");
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+
+ return ret;
+}
+
+int cc33xx_cmd_set_ap_key(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u16 action, u8 id, u8 key_type, u8 key_size,
+ const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16)
+{
+ struct cc33xx_cmd_set_keys *cmd;
+ int ret = 0;
+ u8 lid_type;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ if (hlid == wlvif->ap.bcast_hlid) {
+ if (key_type == KEY_WEP)
+ lid_type = WEP_DEFAULT_LID_TYPE;
+ else
+ lid_type = BROADCAST_LID_TYPE;
+ } else {
+ lid_type = UNICAST_LID_TYPE;
+ }
+
+ cc33xx_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d hlid: %d",
+ (int)action, (int)id, (int)lid_type,
+ (int)key_type, (int)hlid);
+
+ cmd->lid_key_type = lid_type;
+ cmd->hlid = hlid;
+ cmd->key_action = cpu_to_le16(action);
+ cmd->key_size = key_size;
+ cmd->key_type = key_type;
+ cmd->key_id = id;
+ cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
+ cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
+
+ if (key_type == KEY_TKIP) {
+ /* We get the key in the following form:
+ * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
+ * but the target is expecting:
+ * TKIP - RX MIC - TX MIC
+ */
+ memcpy(cmd->key, key, 16);
+ memcpy(cmd->key + 16, key + 24, 8);
+ memcpy(cmd->key + 24, key + 16, 8);
+ } else {
+ memcpy(cmd->key, key, key_size);
+ }
+
+ cc33xx_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd));
+
+ ret = cc33xx_cmd_send(cc, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_warning("could not set ap keys");
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+ return ret;
+}
+
+int cc33xx_cmd_set_peer_state(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 hlid)
+{
+ struct cc33xx_cmd_set_peer_state *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd set peer state (hlid=%d)", hlid);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->hlid = hlid;
+ cmd->state = CC33XX_CMD_STA_STATE_CONNECTED;
+
+ ret = cc33xx_cmd_send(cc, CMD_SET_LINK_CONNECTION_STATE,
+ cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send set peer state command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_add_peer(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct ieee80211_sta *sta, u8 *hlid, u8 is_connected)
+{
+ struct cc33xx_cmd_add_peer *cmd;
+
+ struct cc33xx_cmd_complete_add_peer *command_complete =
+ (struct cc33xx_cmd_complete_add_peer *)&cc->command_result;
+
+ int i, ret;
+ u32 sta_rates;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd add peer is ap %d", is_connected);
+ cmd->is_connected = is_connected;
+ cmd->role_id = wlvif->role_id;
+ cmd->role_type = CC33XX_ROLE_AP;
+ cmd->link_type = 1;
+
+ memcpy(cmd->addr, sta->addr, ETH_ALEN);
+ cmd->bss_index = CC33XX_AP_BSS_INDEX;
+ cmd->aid = cpu_to_le16(sta->aid);
+ cmd->sp_len = sta->max_sp;
+ cmd->wmm = sta->wme ? 1 : 0;
+
+ for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) {
+ if (sta->wme && (sta->uapsd_queues & BIT(i))) {
+ cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY - 1 - i] =
+ CC33XX_PSD_UPSD_TRIGGER;
+ } else {
+ cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY - 1 - i] =
+ CC33XX_PSD_LEGACY;
+ }
+ }
+
+ sta_rates = sta->deflink.supp_rates[wlvif->band];
+ if (sta->deflink.ht_cap.ht_supported) {
+ sta_rates |=
+ (sta->deflink.ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
+ (sta->deflink.ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
+ }
+
+ cmd->supported_rates =
+ cpu_to_le32(cc33xx_tx_enabled_rates_get(cc, sta_rates,
+ wlvif->band));
+
+ if (!cmd->supported_rates) {
+ cc33xx_debug(DEBUG_CMD,
+ "peer has no supported rates yet, configuring basic rates: 0x%x",
+ wlvif->basic_rate_set);
+ cmd->supported_rates = cpu_to_le32(wlvif->basic_rate_set);
+ }
+
+ cc33xx_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
+ cmd->supported_rates, sta->uapsd_queues);
+
+ if (sta->deflink.ht_cap.ht_supported) {
+ cmd->ht_capabilities = cpu_to_le32(sta->deflink.ht_cap.cap);
+ cmd->ht_capabilities |= cpu_to_le32(CC33XX_HT_CAP_HT_OPERATION);
+ cmd->ampdu_params = sta->deflink.ht_cap.ampdu_factor |
+ sta->deflink.ht_cap.ampdu_density;
+ }
+
+ cmd->has_he = sta->deflink.he_cap.has_he;
+ cmd->mfp = sta->mfp;
+ ret = cc33xx_cmd_send(cc, CMD_ADD_PEER, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd add peer");
+ goto out_free;
+ }
+
+ if (hlid) {
+ if (le16_to_cpu(command_complete->header.status) == CMD_STATUS_SUCCESS) {
+ *hlid = command_complete->hlid;
+ cc->links[*hlid].allocated_pkts = 0;
+ cc->session_ids[*hlid] = command_complete->session_id;
+ cc33xx_debug(DEBUG_CMD, "new peer hlid=%d session_ids=%d",
+ command_complete->hlid, command_complete->session_id);
+ } else {
+ ret = -EMLINK;
+ }
+ } else {
+ cc33xx_debug(DEBUG_CMD, "update peer done !");
+ }
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_remove_peer(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 hlid)
+{
+ struct cc33xx_cmd_remove_peer *cmd;
+ int ret;
+ bool timeout = false;
+
+ cc33xx_debug(DEBUG_CMD, "cmd remove peer %d", (int)hlid);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->hlid = hlid;
+
+ cmd->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to initiate cmd remove peer");
+ goto out_free;
+ }
+
+ ret = cc33xx_wait_for_event(cc, CC33XX_EVENT_PEER_REMOVE_COMPLETE,
+ &timeout);
+
+ /* We are ok with a timeout here. The event is sometimes not sent
+ * due to a firmware bug. In case of another error (like SDIO timeout)
+ * queue a recovery.
+ */
+ if (ret < 0)
+ cc33xx_queue_recovery_work(cc);
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+static int cc33xx_get_reg_conf_ch_idx(enum nl80211_band band, u16 ch)
+{
+ /* map the given band/channel to the respective predefined
+ * bit expected by the fw
+ */
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ /* channels 1..14 are mapped to 0..13 */
+ if (ch >= 1 && ch <= 14)
+ return ch - 1;
+ break;
+ case NL80211_BAND_5GHZ:
+ switch (ch) {
+ case 8 ... 16:
+ /* channels 8,12,16 are mapped to 18,19,20 */
+ return 18 + (ch - 8) / 4;
+ case 34 ... 48:
+ /* channels 34,36..48 are mapped to 21..28 */
+ return 21 + (ch - 34) / 2;
+ case 52 ... 64:
+ /* channels 52,56..64 are mapped to 29..32 */
+ return 29 + (ch - 52) / 4;
+ case 100 ... 140:
+ /* channels 100,104..140 are mapped to 33..43 */
+ return 33 + (ch - 100) / 4;
+ case 149 ... 165:
+ /* channels 149,153..165 are mapped to 44..48 */
+ return 44 + (ch - 149) / 4;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ cc33xx_error("%s: unknown band/channel: %d/%d", __func__, band, ch);
+ return -1;
+}
+
+void cc33xx_set_pending_regdomain_ch(struct cc33xx *cc, u16 channel,
+ enum nl80211_band band)
+{
+ int ch_bit_idx = 0;
+
+ if (!(cc->quirks & CC33XX_QUIRK_REGDOMAIN_CONF))
+ return;
+
+ ch_bit_idx = cc33xx_get_reg_conf_ch_idx(band, channel);
+
+ if (ch_bit_idx >= 0 && ch_bit_idx <= CC33XX_MAX_CHANNELS)
+ __set_bit_le(ch_bit_idx, (long *)cc->reg_ch_conf_pending);
+}
+
+int cc33xx_cmd_regdomain_config_locked(struct cc33xx *cc)
+{
+ struct cc33xx_cmd_regdomain_dfs_config *cmd = NULL;
+ int ret = 0, i, b, ch_bit_idx;
+ __le32 tmp_ch_bitmap[2] __aligned(sizeof(unsigned long));
+ struct wiphy *wiphy = cc->hw->wiphy;
+ struct ieee80211_supported_band *band;
+ bool timeout = false;
+
+ return 0;
+
+ if (!(cc->quirks & CC33XX_QUIRK_REGDOMAIN_CONF))
+ return 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd reg domain config");
+
+ memcpy(tmp_ch_bitmap, cc->reg_ch_conf_pending, sizeof(tmp_ch_bitmap));
+
+ for (b = NL80211_BAND_2GHZ; b <= NL80211_BAND_5GHZ; b++) {
+ band = wiphy->bands[b];
+ for (i = 0; i < band->n_channels; i++) {
+ struct ieee80211_channel *channel = &band->channels[i];
+ u16 ch = channel->hw_value;
+ u32 flags = channel->flags;
+
+ if (flags & (IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_IR))
+ continue;
+
+ if ((flags & IEEE80211_CHAN_RADAR) &&
+ channel->dfs_state != NL80211_DFS_AVAILABLE)
+ continue;
+
+ ch_bit_idx = cc33xx_get_reg_conf_ch_idx(b, ch);
+ if (ch_bit_idx < 0)
+ continue;
+
+ __set_bit_le(ch_bit_idx, (long *)tmp_ch_bitmap);
+ }
+ }
+
+ if (!memcmp(tmp_ch_bitmap, cc->reg_ch_conf_last, sizeof(tmp_ch_bitmap)))
+ goto out;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->ch_bit_map1 = tmp_ch_bitmap[0];
+ cmd->ch_bit_map2 = tmp_ch_bitmap[1];
+ cmd->dfs_region = cc->dfs_region;
+
+ cc33xx_debug(DEBUG_CMD,
+ "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
+ cmd->ch_bit_map1, cmd->ch_bit_map2);
+
+ ret = cc33xx_cmd_send(cc, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send reg domain dfs config");
+ goto out;
+ }
+
+ ret = cc33xx_wait_for_event(cc, CC33XX_EVENT_DFS_CONFIG_COMPLETE,
+ &timeout);
+
+ if (ret < 0 || timeout) {
+ cc33xx_error("reg domain conf %serror",
+ timeout ? "completion " : "");
+ ret = timeout ? -ETIMEDOUT : ret;
+ goto out;
+ }
+
+ memcpy(cc->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap));
+ memset(cc->reg_ch_conf_pending, 0, sizeof(cc->reg_ch_conf_pending));
+
+out:
+ kfree(cmd);
+ return ret;
+}
+
+int cc33xx_cmd_config_fwlog(struct cc33xx *cc)
+{
+ struct cc33xx_cmd_config_fwlog *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd config firmware logger");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->logger_mode = cc->conf.host_conf.fwlog.mode;
+ cmd->log_severity = cc->conf.host_conf.fwlog.severity;
+ cmd->timestamp = cc->conf.host_conf.fwlog.timestamp;
+ cmd->output = cc->conf.host_conf.fwlog.output;
+ cmd->threshold = cc->conf.host_conf.fwlog.threshold;
+
+ ret = cc33xx_cmd_send(cc, CMD_CONFIG_FWLOGGER, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send config firmware logger command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+static int cc33xx_cmd_roc(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 role_id, enum nl80211_band band, u8 channel)
+{
+ struct cc33xx_cmd_roc *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);
+
+ if (WARN_ON(role_id == CC33XX_INVALID_ROLE_ID))
+ return -EINVAL;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->role_id = role_id;
+ cmd->channel = channel;
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ cmd->band = CC33XX_BAND_2_4GHZ;
+ break;
+ case NL80211_BAND_5GHZ:
+ cmd->band = CC33XX_BAND_5GHZ;
+ break;
+ default:
+ cc33xx_error("roc - unknown band: %d", (int)wlvif->band);
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ ret = cc33xx_cmd_send(cc, CMD_REMAIN_ON_CHANNEL, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send ROC command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+static int cc33xx_cmd_croc(struct cc33xx *cc, u8 role_id)
+{
+ struct cc33xx_cmd_croc *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd croc (%d)", role_id);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ cmd->role_id = role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_CANCEL_REMAIN_ON_CHANNEL, cmd,
+ sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send ROC command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int cc33xx_roc(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 role_id,
+ enum nl80211_band band, u8 channel)
+{
+ int ret = 0;
+
+ if (WARN_ON(test_bit(role_id, cc->roc_map)))
+ return 0;
+
+ ret = cc33xx_cmd_roc(cc, wlvif, role_id, band, channel);
+ if (ret < 0)
+ goto out;
+
+ __set_bit(role_id, cc->roc_map);
+out:
+ return ret;
+}
+
+int cc33xx_croc(struct cc33xx *cc, u8 role_id)
+{
+ int ret = 0;
+
+ if (WARN_ON(!test_bit(role_id, cc->roc_map)))
+ return 0;
+
+ ret = cc33xx_cmd_croc(cc, role_id);
+ if (ret < 0)
+ goto out;
+
+ __clear_bit(role_id, cc->roc_map);
+
+ /* Rearm the tx watchdog when removing the last ROC. This prevents
+ * recoveries due to just finished ROCs - when Tx hasn't yet had
+ * a chance to get out.
+ */
+ if (find_first_bit(cc->roc_map, CC33XX_MAX_ROLES) >= CC33XX_MAX_ROLES)
+ cc33xx_rearm_tx_watchdog_locked(cc);
+out:
+ return ret;
+}
+
+int cc33xx_cmd_stop_channel_switch(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct cc33xx_cmd_stop_channel_switch *cmd;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "cmd stop channel switch");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_STOP_CHANNEL_SWITCH,
+ cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to stop channel switch command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+/* start dev role and roc on its channel */
+int cc33xx_start_dev(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum nl80211_band band, int channel)
+{
+ int ret;
+
+ if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
+ wlvif->bss_type == BSS_TYPE_IBSS)))
+ return -EINVAL;
+
+ /* the dev role is already started for p2p mgmt interfaces */
+
+ if (!cc33xx_is_p2p_mgmt(wlvif)) {
+ ret = cc33xx_cmd_role_enable(cc,
+ cc33xx_wlvif_to_vif(wlvif)->addr,
+ CC33XX_ROLE_DEVICE,
+ &wlvif->dev_role_id);
+ if (ret < 0)
+ goto out;
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd role start dev");
+ ret = cc33xx_cmd_role_start_dev(cc, wlvif, band, channel);
+ if (ret < 0)
+ goto out_disable;
+
+ cc33xx_debug(DEBUG_CMD, "cmd roc");
+ ret = cc33xx_roc(cc, wlvif, wlvif->dev_role_id, band, channel);
+ if (ret < 0)
+ goto out_stop;
+
+ return 0;
+
+out_stop:
+ cc333xx_cmd_role_stop_dev(cc, wlvif);
+out_disable:
+ if (!cc33xx_is_p2p_mgmt(wlvif))
+ cc33xx_cmd_role_disable(cc, &wlvif->dev_role_id);
+out:
+ return ret;
+}
+
+/* croc dev hlid, and stop the role */
+int cc33xx_stop_dev(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ int ret;
+
+ if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
+ wlvif->bss_type == BSS_TYPE_IBSS)))
+ return -EINVAL;
+
+ /* flush all pending packets */
+ ret = cc33xx_tx_work_locked(cc);
+ if (ret < 0)
+ goto out;
+
+ if (test_bit(wlvif->dev_role_id, cc->roc_map)) {
+ ret = cc33xx_croc(cc, wlvif->dev_role_id);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = cc333xx_cmd_role_stop_dev(cc, wlvif);
+ if (ret < 0)
+ goto out;
+
+ if (!cc33xx_is_p2p_mgmt(wlvif)) {
+ ret = cc33xx_cmd_role_disable(cc, &wlvif->dev_role_id);
+ if (ret < 0)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int cc33xx_cmd_generic_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 feature, u8 enable, u8 value)
+{
+ struct cc33xx_cmd_generic_cfg *cmd;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD,
+ "cmd generic cfg (role %d feature %d enable %d value %d)",
+ wlvif->role_id, feature, enable, value);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->role_id = wlvif->role_id;
+ cmd->feature = feature;
+ cmd->enable = enable;
+ cmd->value = value;
+
+ ret = cc33xx_cmd_send(cc, CMD_GENERIC_CFG, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send generic cfg command");
+ goto out_free;
+ }
+out_free:
+ kfree(cmd);
+ return ret;
+}
+
+int cmd_channel_switch(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct cmd_channel_switch *cmd;
+ u32 supported_rates;
+ int ret;
+
+ cc33xx_debug(DEBUG_ACX, "cmd channel switch (role_id=%d, new channel=%d, count=%d, block tx=%d",
+ wlvif->role_id, ch_switch->chandef.chan->hw_value,
+ ch_switch->count, ch_switch->block_tx);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->role_id = wlvif->role_id;
+ cmd->channel = ch_switch->chandef.chan->hw_value;
+ cmd->switch_time = ch_switch->count;
+ cmd->stop_tx = ch_switch->block_tx;
+
+ switch (ch_switch->chandef.chan->band) {
+ case NL80211_BAND_2GHZ:
+ cmd->band = CC33XX_BAND_2_4GHZ;
+ break;
+ case NL80211_BAND_5GHZ:
+ cmd->band = CC33XX_BAND_5GHZ;
+ break;
+ default:
+ cc33xx_error("invalid channel switch band: %d",
+ ch_switch->chandef.chan->band);
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES;
+ supported_rates |= wlvif->rate_set;
+ if (wlvif->p2p)
+ supported_rates &= ~CONF_TX_CCK_RATES;
+ cmd->local_supported_rates = cpu_to_le32(supported_rates);
+ cmd->channel_type = wlvif->channel_type;
+
+ ret = cc33xx_cmd_send(cc, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send channel switch command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+out:
+ return ret;
+}
+
+int cmd_dfs_master_restart(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ struct cmd_dfs_master_restart *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd dfs master restart (role %d)",
+ wlvif->role_id);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->role_id = wlvif->role_id;
+
+ ret = cc33xx_cmd_send(cc, CMD_DFS_MASTER_RESTART,
+ cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send dfs master restart command");
+ goto out_free;
+ }
+out_free:
+ kfree(cmd);
+ return ret;
+}
+
+int cmd_set_cac(struct cc33xx *cc, struct cc33xx_vif *wlvif, bool start)
+{
+ struct cmd_cac_start *cmd;
+ int ret = 0;
+
+ cc33xx_debug(DEBUG_CMD, "cmd cac (channel %d) %s",
+ wlvif->channel, start ? "start" : "stop");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->role_id = wlvif->role_id;
+ cmd->channel = wlvif->channel;
+ if (wlvif->band == NL80211_BAND_5GHZ)
+ cmd->band = CC33XX_BAND_5GHZ;
+ cmd->bandwidth = cc33xx_get_native_channel_type(wlvif->channel_type);
+
+ ret = cc33xx_cmd_send(cc, start ? CMD_CAC_START : CMD_CAC_STOP,
+ cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send cac command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+ return ret;
+}
+
+int cmd_set_bd_addr(struct cc33xx *cc, u8 *bd_addr)
+{
+ struct cmd_set_bd_addr *cmd;
+ int ret = 0;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(cmd->bd_addr, bd_addr, sizeof(cmd->bd_addr));
+
+ ret = cc33xx_cmd_send(cc, CMD_SET_BD_ADDR, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to set BD address");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+out:
+ return ret;
+}
+
+int cmd_get_device_info(struct cc33xx *cc, u8 *info_buffer, size_t buffer_len)
+{
+ struct cc33xx_cmd_get_device_info *cmd;
+ int ret = 0;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ ret = cc33xx_cmd_send(cc, CMD_BM_READ_DEVICE_INFO, cmd,
+ sizeof(*cmd), sizeof(*cmd));
+ if (ret < 0) {
+ cc33xx_error("Device info command failure ");
+ } else {
+ WARN_ON(buffer_len > sizeof(cmd->device_info));
+ memcpy(info_buffer, cmd->device_info, buffer_len);
+ }
+
+ kfree(cmd);
+
+ return ret;
+}
+
+int cmd_download_container_chunk(struct cc33xx *cc, u8 *chunk,
+ size_t chunk_len, bool is_last_chunk)
+{
+ struct cc33xx_cmd_container_download *cmd;
+ const size_t command_size = sizeof(*cmd) + chunk_len;
+ int ret;
+ bool is_sync_transfer = !is_last_chunk;
+
+ cmd = kzalloc(command_size, GFP_KERNEL);
+
+ if (!cmd) {
+ cc33xx_error("Chunk buffer allocation failure");
+ return -ENOMEM;
+ }
+
+ memcpy(cmd->payload, chunk, chunk_len);
+ cmd->length = cpu_to_le32(chunk_len);
+
+ if (is_last_chunk) {
+ cc33xx_debug(DEBUG_BOOT, "Suspending IRQ while device reboots");
+ cc33xx_disable_interrupts_nosync(cc);
+ }
+
+ ret = __cc33xx_cmd_send(cc, CMD_CONTAINER_DOWNLOAD, cmd,
+ command_size, sizeof(u32), is_sync_transfer);
+
+ kfree(cmd);
+
+ if (is_last_chunk) {
+ msleep(CC33XX_REBOOT_TIMEOUT_MSEC);
+ cc33xx_debug(DEBUG_BOOT, "Resuming IRQ");
+ cc33xx_enable_interrupts(cc);
+ }
+
+ return ret;
+}
diff --git a/drivers/net/wireless/ti/cc33xx/cmd.h b/drivers/net/wireless/ti/cc33xx/cmd.h
new file mode 100644
index 000000000000..f6e4877cf7b4
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/cmd.h
@@ -0,0 +1,700 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __CMD_H__
+#define __CMD_H__
+
+#include "cc33xx.h"
+
+struct acx_header;
+
+enum buffer_size {
+ INI_MAX_BUFFER_SIZE,
+ CMD_MAX_BUFFER_SIZE
+};
+
+int cc33xx_set_max_buffer_size(struct cc33xx *cc, enum buffer_size max_buffer_size);
+int cc33xx_cmd_send(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, size_t res_len);
+int cc33xx_cmd_role_enable(struct cc33xx *cc, u8 *addr,
+ u8 role_type, u8 *role_id);
+int cc33xx_cmd_role_disable(struct cc33xx *cc, u8 *role_id);
+int cc33xx_cmd_role_start_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_stop_sta(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_start_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_stop_ap(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_role_start_ibss(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_start_dev(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ enum nl80211_band band, int channel);
+int cc33xx_stop_dev(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_cmd_test(struct cc33xx *cc, void *buf, size_t buf_len, u8 answer);
+int cc33xx_cmd_interrogate(struct cc33xx *cc, u16 id, void *buf,
+ size_t cmd_len, size_t res_len);
+int cc33xx_cmd_debug_inter(struct cc33xx *cc, u16 id, void *buf,
+ size_t cmd_len, size_t res_len);
+int cc33xx_cmd_configure(struct cc33xx *cc, u16 id, void *buf, size_t len);
+int cc33xx_cmd_debug(struct cc33xx *cc, u16 id, void *buf, size_t len);
+int cc33xx_cmd_configure_failsafe(struct cc33xx *cc, u16 id, void *buf,
+ size_t len, unsigned long valid_rets);
+int cc33xx_cmd_ps_mode(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 ps_mode, u16 auto_ps_timeout);
+int cc33xx_cmd_set_default_wep_key(struct cc33xx *cc, u8 id, u8 hlid);
+int cc33xx_cmd_set_sta_key(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u16 action, u8 id, u8 key_type,
+ u8 key_size, const u8 *key, const u8 *addr,
+ u32 tx_seq_32, u16 tx_seq_16);
+int cc33xx_cmd_set_ap_key(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u16 action, u8 id, u8 key_type, u8 key_size,
+ const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16);
+int cc33xx_cmd_set_peer_state(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 hlid);
+int cc33xx_roc(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 role_id,
+ enum nl80211_band band, u8 channel);
+int cc33xx_croc(struct cc33xx *cc, u8 role_id);
+int cc33xx_cmd_add_peer(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct ieee80211_sta *sta, u8 *hlid, u8 is_connected);
+int cc33xx_cmd_remove_peer(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 hlid);
+void cc33xx_set_pending_regdomain_ch(struct cc33xx *cc, u16 channel,
+ enum nl80211_band band);
+int cc33xx_cmd_regdomain_config_locked(struct cc33xx *cc);
+int cc33xx_cmd_generic_cfg(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ u8 feature, u8 enable, u8 value);
+int cc33xx_cmd_config_fwlog(struct cc33xx *cc);
+int cc33xx_cmd_stop_channel_switch(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_set_link(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 link);
+void cc33xx_clear_link(struct cc33xx *cc, struct cc33xx_vif *wlvif, u8 *hlid);
+int cc33xx_cmd_role_start_transceiver(struct cc33xx *cc, u8 role_id);
+int cc33xx_cmd_role_stop_transceiver(struct cc33xx *cc);
+int cc33xx_cmd_plt_enable(struct cc33xx *cc, u8 role_id);
+int cc33xx_cmd_plt_disable(struct cc33xx *cc);
+int cmd_channel_switch(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct ieee80211_channel_switch *ch_switch);
+int cmd_dfs_master_restart(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cmd_set_cac(struct cc33xx *cc, struct cc33xx_vif *wlvif, bool start);
+int cmd_set_bd_addr(struct cc33xx *cc, u8 *bd_addr);
+int cmd_get_device_info(struct cc33xx *cc, u8 *info_buffer, size_t buffer_len);
+int cmd_download_container_chunk(struct cc33xx *cc, u8 *chunk,
+ size_t chunk_len, bool is_last_chunk);
+
+enum cc33xx_cmd {
+ CMD_EMPTY,
+ CMD_SET_KEYS = 1,
+ CMD_SET_LINK_CONNECTION_STATE = 2,
+
+ CMD_CHANNEL_SWITCH = 3,
+ CMD_STOP_CHANNEL_SWITCH = 4,
+
+ CMD_REMAIN_ON_CHANNEL = 5,
+ CMD_CANCEL_REMAIN_ON_CHANNEL = 6,
+
+ CMD_START_DHCP_MGMT_SEQ = 7,
+ CMD_STOP_DHCP_MGMT_SEQ = 8,
+
+ CMD_START_SECURITY_MGMT_SEQ = 9,
+ CMD_STOP_SECURITY_MGMT_SEQ = 10,
+
+ CMD_START_ARP_MGMT_SEQ = 11,
+ CMD_STOP_ARP_MGMT_SEQ = 12,
+
+ CMD_START_DNS_MGMT_SEQ = 13,
+ CMD_STOP_DNS_MGMT_SEQ = 14,
+
+ /* Access point commands */
+ CMD_ADD_PEER = 15,
+ CMD_REMOVE_PEER = 16,
+
+ /* Role API */
+ CMD_ROLE_ENABLE = 17,
+ CMD_ROLE_DISABLE = 18,
+ CMD_ROLE_START = 19,
+ CMD_ROLE_STOP = 20,
+
+ CMD_AP_SET_BEACON_INFO = 21, /* Set AP beacon template */
+
+ /* Managed sequence of sending deauth / disassoc frame */
+ CMD_SEND_DEAUTH_DISASSOC = 22,
+
+ CMD_SCHED_STATE_EVENT = 23,
+ CMD_SCAN = 24,
+ CMD_STOP_SCAN = 25,
+ CMD_SET_PROBE_IE = 26,
+
+ CMD_CONFIGURE = 27,
+ CMD_INTERROGATE = 28,
+
+ CMD_DEBUG = 29,
+ CMD_DEBUG_READ = 30,
+
+ CMD_TEST_MODE = 31,
+ CMD_PLT_ENABLE = 32,
+ CMD_PLT_DISABLE = 33,
+ CMD_CONNECTION_SCAN_SSID_CFG = 34,
+ CMD_BM_READ_DEVICE_INFO = 35,
+ CMD_CONTAINER_DOWNLOAD = 36,
+ CMD_DOWNLOAD_INI_PARAMS = 37,
+ CMD_SET_BD_ADDR = 38,
+ CMD_BLE_COMMANDS = 39,
+
+ CMD_LAST_SUPPORTED_COMMAND,
+
+ /* The following commands are legacy and are not yet supported */
+
+ CMD_SET_PS_MODE,
+ CMD_DFS_CHANNEL_CONFIG,
+ CMD_CONFIG_FWLOGGER,
+ CMD_START_FWLOGGER,
+ CMD_STOP_FWLOGGER,
+ CMD_GENERIC_CFG,
+ CMD_DFS_MASTER_RESTART,
+ CMD_CAC_START,
+ CMD_CAC_STOP,
+ CMD_DFS_RADAR_DETECTION_DEBUG,
+
+ MAX_COMMAND_ID_CC33xx = 0x7FFF,
+};
+
+#define MAX_CMD_PARAMS 572
+
+/* unit ms */
+#define CC33XX_COMMAND_TIMEOUT 2000
+#define CC33XX_CMD_TEMPL_MAX_SIZE 512
+#define CC33XX_EVENT_TIMEOUT 5000
+
+struct cc33xx_cmd_header {
+ struct NAB_header NAB_header;
+ __le16 id;
+ __le16 status;
+
+ /* payload */
+ u8 data[];
+} __packed;
+
+#define CC33XX_CMD_MAX_PARAMS 572
+
+struct cc33xx_command {
+ struct cc33xx_cmd_header header;
+ u8 parameters[CC33XX_CMD_MAX_PARAMS];
+} __packed;
+
+enum {
+ CMD_MAILBOX_IDLE = 0,
+ CMD_STATUS_SUCCESS = 1,
+ CMD_STATUS_UNKNOWN_CMD = 2,
+ CMD_STATUS_UNKNOWN_IE = 3,
+ CMD_STATUS_REJECT_MEAS_SG_ACTIVE = 11,
+ CMD_STATUS_RX_BUSY = 13,
+ CMD_STATUS_INVALID_PARAM = 14,
+ CMD_STATUS_TEMPLATE_TOO_LARGE = 15,
+ CMD_STATUS_OUT_OF_MEMORY = 16,
+ CMD_STATUS_STA_TABLE_FULL = 17,
+ CMD_STATUS_RADIO_ERROR = 18,
+ CMD_STATUS_WRONG_NESTING = 19,
+ CMD_STATUS_TIMEOUT = 21, /* Driver internal use.*/
+ CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/
+ CMD_STATUS_TEMPLATE_OOM = 23,
+ CMD_STATUS_NO_RX_BA_SESSION = 24,
+
+ MAX_COMMAND_STATUS
+};
+
+enum {
+ BSS_TYPE_IBSS = 0,
+ BSS_TYPE_STA_BSS = 2,
+ BSS_TYPE_AP_BSS = 3,
+ MAX_BSS_TYPE = 0xFF
+};
+
+struct cc33xx_cmd_role_enable {
+ struct cc33xx_cmd_header header;
+
+ u8 role_type;
+ u8 mac_address[ETH_ALEN];
+ u8 padding;
+} __packed;
+
+struct command_complete_header {
+ __le16 id;
+ __le16 status;
+
+ /* payload */
+ u8 data[];
+} __packed;
+
+struct cc33xx_cmd_complete_role_enable {
+ struct command_complete_header header;
+ u8 role_id;
+ u8 padding[3];
+} __packed;
+
+struct cc33xx_cmd_role_disable {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 padding[3];
+} __packed;
+
+enum cc33xx_band {
+ CC33XX_BAND_2_4GHZ = 0,
+ CC33XX_BAND_5GHZ = 1,
+ CC33XX_BAND_JAPAN_4_9_GHZ = 2,
+ CC33XX_BAND_DEFAULT = CC33XX_BAND_2_4GHZ,
+ CC33XX_BAND_INVALID = 0x7E,
+ CC33XX_BAND_MAX_RADIO = 0x7F,
+};
+
+enum cc33xx_channel_type {
+ CC33XX_CHAN_NO_HT,
+ CC33XX_CHAN_HT20,
+ CC33XX_CHAN_HT40MINUS,
+ CC33XX_CHAN_HT40PLUS
+};
+
+struct cc33xx_cmd_role_start {
+ struct cc33xx_cmd_header header;
+ u8 role_id;
+ u8 role_type;
+ u8 band;
+ u8 channel;
+
+ u8 channel_type;
+
+ union {
+ struct {
+ u8 padding_1[54];
+ } __packed device;
+ /* sta & p2p_cli use the same struct */
+ struct {
+ u8 bssid[ETH_ALEN];
+
+ __le32 remote_rates; /* remote supported rates */
+
+ /* The target uses this field to determine the rate at
+ * which to transmit control frame responses (such as
+ * ACK or CTS frames).
+ */
+ __le32 basic_rate_set;
+ __le32 local_rates; /* local supported rates */
+
+ u8 ssid_type;
+ u8 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+ __le16 beacon_interval; /* in TBTTs */
+ } __packed sta;
+ struct {
+ u8 bssid[ETH_ALEN];
+ u8 hlid; /* data hlid */
+ u8 dtim_interval;
+ __le32 remote_rates; /* remote supported rates */
+
+ __le32 basic_rate_set;
+ __le32 local_rates; /* local supported rates */
+
+ u8 ssid_type;
+ u8 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+ __le16 beacon_interval; /* in TBTTs */
+
+ u8 padding_1[2];
+ } __packed ibss;
+ /* ap & p2p_go use the same struct */
+ struct {
+ __le16 beacon_interval; /* in TBTTs */
+
+ __le32 basic_rate_set;
+ __le32 local_rates; /* local supported rates */
+
+ u8 dtim_interval;
+ /* ap supports wmm (note that there is additional
+ * per-sta wmm configuration)
+ */
+ u8 wmm;
+ u8 padding_1[42];
+ } __packed ap;
+ };
+ u8 padding;
+} __packed;
+
+struct cc33xx_cmd_complete_role_start {
+ struct command_complete_header header;
+ union {
+ struct {
+ u8 hlid;
+ u8 session;
+ u8 padding[2];
+ } __packed sta;
+ struct {
+ /* The host link id for the AP's global queue */
+ u8 global_hlid;
+ /* The host link id for the AP's broadcast queue */
+ u8 broadcast_hlid;
+ u8 bcast_session_id;
+ u8 global_session_id;
+ } __packed ap;
+ };
+} __packed;
+
+struct cc33xx_cmd_role_stop {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 padding[3];
+} __packed;
+
+struct cmd_enabledisable_path {
+ struct cc33xx_cmd_header header;
+
+ u8 channel;
+ u8 padding[3];
+} __packed;
+
+enum cc33xx_cmd_ps_mode_e {
+ STATION_AUTO_PS_MODE, /* Dynamic Power Save */
+ STATION_ACTIVE_MODE,
+ STATION_POWER_SAVE_MODE
+};
+
+struct cc33xx_cmd_ps_params {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 ps_mode; /* STATION_* */
+ __le16 auto_ps_timeout;
+} __packed;
+
+/* HW encryption keys */
+#define NUM_ACCESS_CATEGORIES_COPY 4
+
+enum cc33xx_cmd_key_action {
+ KEY_ADD_OR_REPLACE = 1,
+ KEY_REMOVE = 2,
+ KEY_SET_ID = 3,
+ MAX_KEY_ACTION = 0xffff,
+};
+
+enum cc33xx_cmd_lid_key_type {
+ UNICAST_LID_TYPE = 0,
+ BROADCAST_LID_TYPE = 1,
+ WEP_DEFAULT_LID_TYPE = 2
+};
+
+enum cc33xx_cmd_key_type {
+ KEY_NONE = 0,
+ KEY_WEP = 1,
+ KEY_TKIP = 2,
+ KEY_AES = 3, /* aes_ccmp_128 */
+ KEY_GEM = 4,
+ KEY_IGTK = 5, /* bip_cmac_128 */
+ KEY_CMAC_256 = 6,
+ KEY_GMAC_128 = 7,
+ KEY_GMAC_256 = 8,
+ KEY_GCMP_256 = 9,
+ KEY_CCMP256 = 10,
+ KEY_GCMP128 = 11,
+};
+
+struct cc33xx_cmd_set_keys {
+ struct cc33xx_cmd_header header;
+
+ /* Indicates whether the HLID is a unicast key set
+ * or broadcast key set. A special value 0xFF is
+ * used to indicate that the HLID is on WEP-default
+ * (multi-hlids). of type cc33xx_cmd_lid_key_type.
+ */
+ u8 hlid;
+
+ /* In WEP-default network (hlid == 0xFF) used to
+ * indicate which network STA/IBSS/AP role should be
+ * changed
+ */
+ u8 lid_key_type;
+
+ /* Key ID - For TKIP and AES key types, this field
+ * indicates the value that should be inserted into
+ * the KeyID field of frames transmitted using this
+ * key entry. For broadcast keys the index use as a
+ * marker for TX/RX key.
+ * For WEP default network (HLID=0xFF), this field
+ * indicates the ID of the key to add or remove.
+ */
+ u8 key_id;
+ u8 reserved_1;
+
+ /* key_action_e */
+ __le16 key_action;
+
+ /* key size in bytes */
+ u8 key_size;
+
+ /* key_type_e */
+ u8 key_type;
+
+ /* This field holds the security key data to add to the STA table */
+ u8 key[MAX_KEY_SIZE];
+ __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
+ __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
+} __packed;
+
+struct cc33xx_cmd_test_header {
+ u8 id;
+ u8 padding[3];
+} __packed;
+
+#define CC33XX_CMD_STA_STATE_CONNECTED 1
+
+struct cc33xx_cmd_set_peer_state {
+ struct cc33xx_cmd_header header;
+
+ u8 hlid;
+ u8 state;
+ u8 padding[2];
+} __packed;
+
+struct cc33xx_cmd_roc {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 channel;
+ u8 band;
+ u8 padding;
+};
+
+struct cc33xx_cmd_croc {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 padding[3];
+};
+
+enum cc33xx_ssid_type {
+ CC33XX_SSID_TYPE_PUBLIC = 0,
+ CC33XX_SSID_TYPE_HIDDEN = 1,
+ CC33XX_SSID_TYPE_ANY = 2,
+};
+
+enum CC33XX_psd_type {
+ CC33XX_PSD_LEGACY = 0,
+ CC33XX_PSD_UPSD_TRIGGER = 1,
+ CC33XX_PSD_LEGACY_PSPOLL = 2,
+ CC33XX_PSD_SAPSD = 3
+};
+
+#define MAX_SIZE_BEACON_TEMP (450)
+struct cc33xx_cmd_set_beacon_info {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ __le16 beacon_len;
+ u8 beacon[MAX_SIZE_BEACON_TEMP];
+ u8 padding[3];
+} __packed;
+
+struct cc33xx_cmd_add_peer {
+ struct cc33xx_cmd_header header;
+
+ u8 is_connected;
+ u8 role_id;
+ u8 role_type;
+ u8 link_type;
+ u8 addr[ETH_ALEN];
+ __le16 aid;
+ u8 psd_type[NUM_ACCESS_CATEGORIES_COPY];
+ __le32 supported_rates;
+ u8 bss_index;
+ u8 sp_len;
+ u8 wmm;
+ __le32 ht_capabilities;
+ u8 ampdu_params;
+
+ /* HE peer support */
+ bool has_he;
+ bool mfp;
+ u8 padding[2];
+} __packed;
+
+struct cc33xx_cmd_complete_add_peer {
+ struct command_complete_header header;
+ u8 hlid;
+ u8 session_id;
+} __packed;
+
+struct cc33xx_cmd_remove_peer {
+ struct cc33xx_cmd_header header;
+ u8 hlid;
+ u8 role_id;
+ u8 padding[2];
+} __packed;
+
+/* Continuous mode - packets are transferred to the host periodically
+ * via the data path.
+ * On demand - Log messages are stored in a cyclic buffer in the
+ * firmware, and only transferred to the host when explicitly requested
+ */
+enum cc33xx_fwlogger_log_mode {
+ CC33XX_FWLOG_CONTINUOUS,
+};
+
+/* Include/exclude timestamps from the log messages */
+enum cc33xx_fwlogger_timestamp {
+ CC33XX_FWLOG_TIMESTAMP_DISABLED,
+ CC33XX_FWLOG_TIMESTAMP_ENABLED
+};
+
+/* Logs can be routed to the debug pinouts (where available), to the host bus
+ * (SDIO/SPI), or dropped
+ */
+enum cc33xx_fwlogger_output {
+ CC33XX_FWLOG_OUTPUT_NONE,
+ CC33XX_FWLOG_OUTPUT_DBG_PINS,
+ CC33XX_FWLOG_OUTPUT_HOST,
+};
+
+struct cc33xx_cmd_regdomain_dfs_config {
+ struct cc33xx_cmd_header header;
+
+ __le32 ch_bit_map1;
+ __le32 ch_bit_map2;
+ u8 dfs_region;
+ u8 padding[3];
+} __packed;
+
+enum cc33xx_generic_cfg_feature {
+ CC33XX_CFG_FEATURE_RADAR_DEBUG = 2,
+};
+
+struct cc33xx_cmd_generic_cfg {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 feature;
+ u8 enable;
+ u8 value;
+} __packed;
+
+struct cc33xx_cmd_config_fwlog {
+ struct cc33xx_cmd_header header;
+
+ /* See enum cc33xx_fwlogger_log_mode */
+ u8 logger_mode;
+
+ /* Minimum log level threshold */
+ u8 log_severity;
+
+ /* Include/exclude timestamps from the log messages */
+ u8 timestamp;
+
+ /* See enum cc33xx_fwlogger_output */
+ u8 output;
+
+ /* Regulates the frequency of log messages */
+ u8 threshold;
+
+ u8 padding[3];
+} __packed;
+
+struct cc33xx_cmd_stop_channel_switch {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 padding[3];
+} __packed;
+
+/* Used to check radio status after calibration */
+#define MAX_TLV_LENGTH 500
+#define TEST_CMD_P2G_CAL 2 /* TX BiP */
+
+struct cc33xx_cmd_cal_p2g {
+ struct cc33xx_cmd_header header;
+
+ struct cc33xx_cmd_test_header test;
+
+ __le32 ver;
+ __le16 len;
+ u8 buf[MAX_TLV_LENGTH];
+ u8 type;
+ u8 padding;
+
+ __le16 radio_status;
+
+ u8 sub_band_mask;
+ u8 padding2;
+} __packed;
+
+struct cmd_channel_switch {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+
+ /* The new serving channel */
+ u8 channel;
+ /* Relative time of the serving channel switch in TBTT units */
+ u8 switch_time;
+ /* Stop the role TX, should expect it after radar detection */
+ u8 stop_tx;
+
+ __le32 local_supported_rates;
+
+ u8 channel_type;
+ u8 band;
+
+ u8 padding[2];
+} __packed;
+
+struct cmd_set_bd_addr {
+ struct cc33xx_cmd_header header;
+
+ u8 bd_addr[ETH_ALEN];
+ u8 padding[2];
+} __packed;
+
+struct cmd_dfs_master_restart {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 padding[3];
+} __packed;
+
+/* cac_start and cac_stop share the same params */
+struct cmd_cac_start {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 channel;
+ u8 band;
+ u8 bandwidth;
+} __packed;
+
+/* PLT structs */
+
+struct cc33xx_cmd_PLT_enable {
+ struct cc33xx_cmd_header header;
+ __le32 dummy;
+};
+
+struct cc33xx_cmd_PLT_disable {
+ struct cc33xx_cmd_header header;
+ __le32 dummy;
+};
+
+struct cc33xx_cmd_ini_params_download {
+ struct cc33xx_cmd_header header;
+ __le32 length;
+ u8 payload[];
+} __packed;
+
+struct cc33xx_cmd_container_download {
+ struct cc33xx_cmd_header header;
+ __le32 length;
+ u8 payload[];
+} __packed;
+
+struct cc33xx_cmd_get_device_info {
+ struct cc33xx_cmd_header header;
+ u8 device_info[700];
+} __packed;
+
+#endif /* __CC33XX_CMD_H__ */
--
2.25.1


2024-05-21 17:24:17

by Nemanov, Michael

[permalink] [raw]
Subject: [PATCH 12/17] Add scan.c, scan.h

From: Michael Nemanov <[email protected]>

Handles the scan process.
Scan starts via cc33xx_op_hw_scan (main.c) which calls cc33xx_scan.
Scan channels are packed and sent to HW where scanning is managed by
FW concurrently to other roles without driver intervention.
Scan results are handled like normal management frames
and are sent to MAC80211. HW notifies driver of scan completion via
dedicated event which triggers a call to cc33xx_scan_completed.

Signed-off-by: Michael Nemanov <[email protected]>
---
drivers/net/wireless/ti/cc33xx/scan.c | 756 ++++++++++++++++++++++++++
drivers/net/wireless/ti/cc33xx/scan.h | 364 +++++++++++++
2 files changed, 1120 insertions(+)
create mode 100644 drivers/net/wireless/ti/cc33xx/scan.c
create mode 100644 drivers/net/wireless/ti/cc33xx/scan.h

diff --git a/drivers/net/wireless/ti/cc33xx/scan.c b/drivers/net/wireless/ti/cc33xx/scan.c
new file mode 100644
index 000000000000..36f5df26a9fa
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/scan.c
@@ -0,0 +1,756 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include "cc33xx.h"
+#include "debug.h"
+#include "cmd.h"
+#include "scan.h"
+#include "tx.h"
+#include "conf.h"
+
+static void cc33xx_adjust_channels(struct scan_param *scan_param,
+ struct cc33xx_scan_channels *cmd_channels,
+ enum scan_request_type scan_type)
+{
+ struct conn_scan_ch_info *ch_info_list;
+ struct conn_scan_dwell_info *dwell_info;
+ struct conn_scan_ch_params *channel;
+ struct conn_scan_ch_params *ch_params_list;
+
+ u8 *passive;
+ u8 *dfs;
+ u8 *active;
+ int i, j;
+ u8 band;
+
+ if (scan_type == SCAN_REQUEST_CONNECT_PERIODIC_SCAN) {
+ ch_info_list = scan_param->u.periodic.channel_list;
+ dwell_info = scan_param->u.periodic.dwell_info;
+ active = (u8 *)&scan_param->u.periodic.active;
+ passive = (u8 *)&scan_param->u.periodic.passive;
+ dfs = (u8 *)&scan_param->u.periodic.dfs;
+ } else {
+ ch_info_list = scan_param->u.one_shot.channel_list;
+ dwell_info = scan_param->u.one_shot.dwell_info;
+ active = (u8 *)&scan_param->u.one_shot.active;
+ passive = (u8 *)&scan_param->u.one_shot.passive;
+ dfs = (u8 *)&scan_param->u.one_shot.dfs;
+ }
+
+ memcpy(passive, cmd_channels->passive, sizeof(cmd_channels->passive));
+ memcpy(active, cmd_channels->active, sizeof(cmd_channels->active));
+ *dfs = cmd_channels->dfs;
+
+ ch_params_list = cmd_channels->channels_2;
+ for (i = 0; i < MAX_CHANNELS_2GHZ; ++i) {
+ ch_info_list[i].channel = ch_params_list[i].channel;
+ ch_info_list[i].flags = ch_params_list[i].flags;
+ ch_info_list[i].tx_power_att = ch_params_list[i].tx_power_att;
+ }
+
+ channel = &ch_params_list[0];
+ band = NL80211_BAND_2GHZ;
+ dwell_info[band].min_duration = channel->min_duration;
+ dwell_info[band].max_duration = channel->max_duration;
+ dwell_info[band].passive_duration = channel->passive_duration;
+
+ ch_params_list = cmd_channels->channels_5;
+ for (j = 0; j < MAX_CHANNELS_5GHZ; ++i, ++j) {
+ ch_info_list[i].channel = ch_params_list[j].channel;
+ ch_info_list[i].flags = ch_params_list[j].flags;
+ ch_info_list[i].tx_power_att = ch_params_list[j].tx_power_att;
+ }
+
+ channel = &ch_params_list[0];
+ band = NL80211_BAND_5GHZ;
+ dwell_info[band].min_duration = channel->min_duration;
+ dwell_info[band].max_duration = channel->max_duration;
+ dwell_info[band].passive_duration = channel->passive_duration;
+}
+
+static int cc33xx_cmd_build_probe_req(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 role_id,
+ u8 scan_type, const u8 *ssid,
+ size_t ssid_len, const u8 *ie0,
+ size_t ie0_len, const u8 *ie1,
+ size_t ie1_len, bool sched_scan)
+{
+ struct ieee80211_vif *vif = cc33xx_wlvif_to_vif(wlvif);
+ struct sk_buff *skb = NULL;
+ struct cc33xx_cmd_set_ies *cmd;
+ int ret;
+
+ cc33xx_debug(DEBUG_SCAN, "build probe request scan_type %d", scan_type);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ skb = ieee80211_probereq_get(cc->hw, vif->addr, ssid,
+ ssid_len, ie0_len + ie1_len);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ if (ie0_len)
+ skb_put_data(skb, ie0, ie0_len);
+
+ if (ie1_len)
+ skb_put_data(skb, ie1, ie1_len);
+
+ cmd->scan_type = scan_type;
+ cmd->role_id = role_id;
+
+ cmd->len = cpu_to_le16(skb->len - sizeof(struct ieee80211_hdr_3addr));
+
+ if (skb->data) {
+ memcpy(cmd->data,
+ skb->data + sizeof(struct ieee80211_hdr_3addr), le16_to_cpu(cmd->len));
+ }
+
+ usleep_range(10000, 11000);
+ ret = cc33xx_cmd_send(cc, CMD_SET_PROBE_IE, cmd, sizeof(*cmd), 0);
+
+ if (ret < 0) {
+ cc33xx_warning("cmd set_template failed: %d", ret);
+ goto out_free;
+ }
+
+out_free:
+ dev_kfree_skb(skb);
+ kfree(cmd);
+out:
+ return ret;
+}
+
+static void cc33xx_started_vifs_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+ bool active = false;
+ int *count = (int *)data;
+
+ /* count active interfaces according to interface type.
+ * checking only bss_conf.idle is bad for some cases, e.g.
+ * we don't want to count sta in p2p_find as active interface.
+ */
+ switch (wlvif->bss_type) {
+ case BSS_TYPE_STA_BSS:
+ if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+ active = true;
+ break;
+
+ case BSS_TYPE_AP_BSS:
+ if (wlvif->cc->active_sta_count > 0)
+ active = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (active)
+ (*count)++;
+}
+
+static int cc33xx_count_started_vifs(struct cc33xx *cc)
+{
+ int count = 0;
+
+ ieee80211_iterate_active_interfaces_atomic(cc->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ cc33xx_started_vifs_iter,
+ &count);
+ return count;
+}
+
+static int cc33xx_scan_get_channels(struct cc33xx *cc,
+ struct ieee80211_channel *req_channels[],
+ u32 n_channels, u32 n_ssids,
+ struct conn_scan_ch_params *channels,
+ u32 band, bool radar, bool passive,
+ int start, int max_channels,
+ u8 *n_pactive_ch, int scan_type)
+{
+ int i, j;
+ u32 flags;
+ bool force_passive = !n_ssids;
+ u32 min_dwell_time_active, max_dwell_time_active;
+ u32 dwell_time_passive, dwell_time_dfs;
+ struct conn_scan_ch_params *ch;
+ struct ieee80211_channel *req_ch;
+
+ /* configure dwell times according to scan type */
+ if (scan_type == SCAN_TYPE_SEARCH) {
+ struct conf_scan_settings *c = &cc->conf.host_conf.scan;
+ bool active_vif_exists = !!cc33xx_count_started_vifs(cc);
+
+ min_dwell_time_active = active_vif_exists ?
+ c->min_dwell_time_active :
+ c->min_dwell_time_active_long;
+ max_dwell_time_active = active_vif_exists ?
+ c->max_dwell_time_active :
+ c->max_dwell_time_active_long;
+ dwell_time_passive = c->dwell_time_passive;
+ dwell_time_dfs = c->dwell_time_dfs;
+ } else {
+ struct conf_sched_scan_settings *c =
+ &cc->conf.host_conf.sched_scan;
+ u32 delta_per_probe;
+
+ delta_per_probe = (band == NL80211_BAND_5GHZ) ?
+ c->dwell_time_delta_per_probe_5 :
+ c->dwell_time_delta_per_probe;
+
+ min_dwell_time_active = c->base_dwell_time +
+ n_ssids * c->num_probe_reqs * delta_per_probe;
+
+ max_dwell_time_active = min_dwell_time_active;
+ max_dwell_time_active += c->max_dwell_time_delta;
+ dwell_time_passive = c->dwell_time_passive;
+ dwell_time_dfs = c->dwell_time_dfs;
+ }
+
+ min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
+ max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
+ dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
+ dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
+
+ for (i = 0, j = start; i < n_channels && j < max_channels; i++) {
+ flags = req_channels[i]->flags;
+ ch = &channels[j];
+ req_ch = req_channels[i];
+
+ if (force_passive)
+ flags |= IEEE80211_CHAN_NO_IR;
+
+ if (req_ch->band == band && !(flags & IEEE80211_CHAN_DISABLED) &&
+ (!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
+ /* if radar is set, we ignore the passive flag */
+ (radar || !!(flags & IEEE80211_CHAN_NO_IR) == passive)) {
+ if (flags & IEEE80211_CHAN_RADAR) {
+ ch->flags |= SCAN_CHANNEL_FLAGS_DFS;
+
+ ch->passive_duration =
+ cpu_to_le16(dwell_time_dfs);
+ } else {
+ ch->passive_duration =
+ cpu_to_le16(dwell_time_passive);
+ }
+
+ ch->min_duration = cpu_to_le16(min_dwell_time_active);
+ ch->max_duration = cpu_to_le16(max_dwell_time_active);
+
+ ch->tx_power_att = req_ch->max_power;
+ ch->channel = req_ch->hw_value;
+
+ if (n_pactive_ch && band == NL80211_BAND_2GHZ &&
+ ch->channel >= 12 && ch->channel <= 14 &&
+ (flags & IEEE80211_CHAN_NO_IR) && !force_passive) {
+ /* pactive channels treated as DFS */
+ ch->flags = SCAN_CHANNEL_FLAGS_DFS;
+
+ /* n_pactive_ch is counted down from the end of
+ * the passive channel list
+ */
+ (*n_pactive_ch)++;
+ cc33xx_debug(DEBUG_SCAN, "n_pactive_ch = %d",
+ *n_pactive_ch);
+ }
+
+ cc33xx_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s",
+ req_ch->center_freq, req_ch->hw_value,
+ req_ch->flags, req_ch->max_power,
+ min_dwell_time_active,
+ max_dwell_time_active,
+ flags & IEEE80211_CHAN_RADAR ? ", DFS" : "",
+ flags & IEEE80211_CHAN_NO_IR ? ", NO-IR" : "");
+ j++;
+ }
+ }
+
+ return j - start;
+}
+
+static bool cc33xx_set_scan_chan_params(struct cc33xx *cc,
+ struct cc33xx_scan_channels *cfg,
+ struct ieee80211_channel *channels[],
+ u32 n_channels, u32 n_ssids,
+ int scan_type)
+{
+ u8 n_pactive_ch = 0;
+
+ cfg->passive[0] = cc33xx_scan_get_channels(cc, channels, n_channels,
+ n_ssids, cfg->channels_2,
+ NL80211_BAND_2GHZ, false,
+ true, 0, MAX_CHANNELS_2GHZ,
+ &n_pactive_ch, scan_type);
+
+ cfg->active[0] = cc33xx_scan_get_channels(cc, channels, n_channels,
+ n_ssids, cfg->channels_2,
+ NL80211_BAND_2GHZ, false,
+ false, cfg->passive[0],
+ MAX_CHANNELS_2GHZ,
+ &n_pactive_ch, scan_type);
+
+ cfg->passive[1] = cc33xx_scan_get_channels(cc, channels, n_channels,
+ n_ssids, cfg->channels_5,
+ NL80211_BAND_5GHZ, false,
+ true, 0, MAX_CHANNELS_5GHZ,
+ &n_pactive_ch, scan_type);
+
+ cfg->dfs = cc33xx_scan_get_channels(cc, channels, n_channels, n_ssids,
+ cfg->channels_5, NL80211_BAND_5GHZ,
+ true, true, cfg->passive[1],
+ MAX_CHANNELS_5GHZ, &n_pactive_ch,
+ scan_type);
+
+ cfg->active[1] = cc33xx_scan_get_channels(cc, channels, n_channels,
+ n_ssids, cfg->channels_5,
+ NL80211_BAND_5GHZ, false,
+ false,
+ cfg->passive[1] + cfg->dfs,
+ MAX_CHANNELS_5GHZ,
+ &n_pactive_ch, scan_type);
+
+ /* 802.11j channels are not supported yet */
+ cfg->passive[2] = 0;
+ cfg->active[2] = 0;
+
+ cfg->passive_active = n_pactive_ch;
+
+ cc33xx_debug(DEBUG_SCAN, "2.4GHz: active %d passive %d",
+ cfg->active[0], cfg->passive[0]);
+ cc33xx_debug(DEBUG_SCAN, "5GHz: active %d passive %d",
+ cfg->active[1], cfg->passive[1]);
+ cc33xx_debug(DEBUG_SCAN, "DFS: %d", cfg->dfs);
+
+ return cfg->passive[0] || cfg->active[0] || cfg->passive[1] ||
+ cfg->active[1] || cfg->dfs || cfg->passive[2] || cfg->active[2];
+}
+
+static int cc33xx_scan_send(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct cfg80211_scan_request *req)
+{
+ struct cc33xx_cmd_scan_params *cmd;
+ struct cc33xx_scan_channels *cmd_channels = NULL;
+ struct cc33xx_ssid *cmd_ssid;
+ u16 alloc_size;
+ int ret;
+ int i;
+
+ alloc_size = sizeof(*cmd) + (sizeof(struct cc33xx_ssid) * req->n_ssids);
+ cmd = kzalloc(alloc_size, GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* scan on the dev role if the regular one is not started */
+ if (cc33xx_is_p2p_mgmt(wlvif))
+ cmd->role_id = wlvif->dev_role_id;
+ else
+ cmd->role_id = wlvif->role_id;
+
+ if (WARN_ON(cmd->role_id == CC33XX_INVALID_ROLE_ID)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd->scan_type = SCAN_REQUEST_ONE_SHOT;
+ cmd->rssi_threshold = -127;
+ cmd->snr_threshold = 0;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ cmd->bssid[i] = req->bssid[i];
+ cmd->ssid_from_list = 0;
+ cmd->filter = 0;
+ WARN_ON(req->n_ssids > 1);
+
+ /* configure channels */
+ cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+ if (!cmd_channels) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cc33xx_set_scan_chan_params(cc, cmd_channels, req->channels,
+ req->n_channels, req->n_ssids,
+ SCAN_TYPE_SEARCH);
+
+ cc33xx_adjust_channels(&cmd->params, cmd_channels, cmd->scan_type);
+ if (req->n_ssids > 0) {
+ cmd->ssid_from_list = 1;
+ cmd->num_of_ssids = req->n_ssids;
+ cmd_ssid = (struct cc33xx_ssid *)((u8 *)cmd + sizeof(*cmd));
+
+ cmd_ssid->len = req->ssids[0].ssid_len;
+ memcpy(cmd_ssid->ssid, req->ssids[0].ssid, cmd_ssid->len);
+ cmd_ssid->type = (req->ssids[0].ssid_len) ?
+ SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
+ }
+
+ ret = cc33xx_cmd_build_probe_req(cc, wlvif, cmd->role_id, cmd->scan_type,
+ req->ssids ? req->ssids[0].ssid : NULL,
+ req->ssids ? req->ssids[0].ssid_len : 0,
+ req->ie, req->ie_len, NULL, 0, false);
+ if (ret < 0) {
+ cc33xx_error("PROBE request template failed");
+ goto out;
+ }
+
+ cc33xx_dump(DEBUG_SCAN, "SCAN: ", cmd, alloc_size);
+
+ ret = cc33xx_cmd_send(cc, CMD_SCAN, cmd, alloc_size, 0);
+ if (ret < 0) {
+ cc33xx_error("SCAN failed");
+ goto out;
+ }
+
+out:
+ kfree(cmd_channels);
+ kfree(cmd);
+ return ret;
+}
+
+static int cc33xx_scan_sched_scan_ssid_list(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif,
+ struct cfg80211_sched_scan_request *req,
+ struct cc33xx_cmd_ssid_list *cmd)
+{
+ struct cfg80211_match_set *sets = req->match_sets;
+ struct cfg80211_ssid *ssids = req->ssids;
+ int ret = 0, i, j, n_match_ssids = 0;
+
+ cc33xx_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list");
+ /* count the match sets that contain SSIDs */
+ for (i = 0; i < req->n_match_sets; i++) {
+ if (sets[i].ssid.ssid_len > 0)
+ n_match_ssids++;
+ }
+
+ /* No filter, no ssids or only bcast ssid */
+ if (!n_match_ssids && (!req->n_ssids ||
+ (req->n_ssids == 1 && req->ssids[0].ssid_len == 0)))
+ goto out;
+
+ cmd->role_id = wlvif->role_id;
+ if (!n_match_ssids) {
+ /* No filter, with ssids */
+
+ for (i = 0; i < req->n_ssids; i++) {
+ cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ?
+ SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
+ cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len;
+ memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid,
+ ssids[i].ssid_len);
+ cmd->n_ssids++;
+ }
+ } else {
+ /* Add all SSIDs from the filters */
+ for (i = 0; i < req->n_match_sets; i++) {
+ /* ignore sets without SSIDs */
+ if (!sets[i].ssid.ssid_len)
+ continue;
+
+ cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC;
+ cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len;
+ memcpy(cmd->ssids[cmd->n_ssids].ssid,
+ sets[i].ssid.ssid, sets[i].ssid.ssid_len);
+ cmd->n_ssids++;
+ }
+ if (req->n_ssids > 1 || (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) {
+ /* Mark all the SSIDs passed in the SSID list as HIDDEN,
+ * so they're used in probe requests.
+ */
+ for (i = 0; i < req->n_ssids; i++) {
+ if (!req->ssids[i].ssid_len)
+ continue;
+
+ for (j = 0; j < cmd->n_ssids; j++) {
+ if (req->ssids[i].ssid_len == cmd->ssids[j].len &&
+ !memcmp(req->ssids[i].ssid,
+ cmd->ssids[j].ssid,
+ req->ssids[i].ssid_len)) {
+ cmd->ssids[j].type =
+ SCAN_SSID_TYPE_HIDDEN;
+ break;
+ }
+ }
+ /* Fail if SSID isn't present in the filters */
+ if (j == cmd->n_ssids) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ }
+ }
+
+ cc33xx_debug(DEBUG_CMD, "cmd sched scan with ssid list %d",
+ cmd->n_ssids);
+ return cmd->n_ssids;
+out:
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int cc33xx_sched_scan_start(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ struct cc33xx_cmd_scan_params *cmd;
+ struct cc33xx_cmd_ssid_list *ssid_list;
+ struct cc33xx_scan_channels *cmd_channels = NULL;
+ struct conf_sched_scan_settings *c = &cc->conf.host_conf.sched_scan;
+ int ret;
+ int n_ssids = 0;
+ int alloc_size = sizeof(*cmd);
+
+ cc33xx_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+ ssid_list = kzalloc(sizeof(*ssid_list), GFP_KERNEL);
+ if (!ssid_list) {
+ ret = -ENOMEM;
+ goto out_ssid_free;
+ }
+
+ n_ssids = cc33xx_scan_sched_scan_ssid_list(cc, wlvif, req, ssid_list);
+ if (n_ssids < 0)
+ return n_ssids;
+
+ cc33xx_debug(DEBUG_CMD, "ssid list num of ssids %d", ssid_list->n_ssids);
+
+ if (n_ssids <= 5) {
+ alloc_size += (n_ssids * sizeof(struct cc33xx_ssid));
+ } else { /* n_ssids > 5 */
+ ssid_list->scan_type = SCAN_REQUEST_CONNECT_PERIODIC_SCAN;
+ ret = cc33xx_cmd_send(cc, CMD_CONNECTION_SCAN_SSID_CFG,
+ ssid_list, sizeof(*ssid_list), 0);
+ if (ret < 0) {
+ cc33xx_error("cmd sched scan ssid list failed");
+ goto out_ssid_free;
+ }
+ }
+
+ cmd = kzalloc(alloc_size, GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ cmd->role_id = wlvif->role_id;
+
+ if (WARN_ON(cmd->role_id == CC33XX_INVALID_ROLE_ID)) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ cmd->scan_type = SCAN_REQUEST_CONNECT_PERIODIC_SCAN;
+ cmd->rssi_threshold = c->rssi_threshold;
+ cmd->snr_threshold = c->snr_threshold;
+
+ cmd->filter = 1;
+ cmd->num_of_ssids = n_ssids;
+
+ cc33xx_debug(DEBUG_CMD, "ssid list num of n_ssids %d", n_ssids);
+ if (n_ssids > 0 && n_ssids <= 5) {
+ cmd->ssid_from_list = 1;
+ memcpy((u8 *)cmd + sizeof(*cmd), ssid_list->ssids,
+ n_ssids * sizeof(struct cc33xx_ssid));
+ }
+
+ cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+ if (!cmd_channels) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ /* configure channels */
+ cc33xx_set_scan_chan_params(cc, cmd_channels, req->channels,
+ req->n_channels, req->n_ssids,
+ SCAN_TYPE_PERIODIC);
+ cc33xx_adjust_channels(&cmd->params, cmd_channels, cmd->scan_type);
+
+ memcpy(cmd->params.u.periodic.sched_scan_plans, req->scan_plans,
+ sizeof(struct sched_scan_plan_cmd) * req->n_scan_plans);
+
+ cmd->params.u.periodic.sched_scan_plans_num = req->n_scan_plans;
+
+ cc33xx_debug(DEBUG_SCAN,
+ "interval[0]: %d, iterations[0]: %d, num_plans: %d",
+ cmd->params.u.periodic.sched_scan_plans[0].interval,
+ cmd->params.u.periodic.sched_scan_plans[0].iterations,
+ cmd->params.u.periodic.sched_scan_plans_num);
+
+ ret = cc33xx_cmd_build_probe_req(cc, wlvif, cmd->role_id, cmd->scan_type,
+ req->ssids ? req->ssids[0].ssid : NULL,
+ req->ssids ? req->ssids[0].ssid_len : 0,
+ ies->ies[NL80211_BAND_2GHZ],
+ ies->len[NL80211_BAND_2GHZ],
+ ies->common_ies,
+ ies->common_ie_len, true);
+
+ if (ret < 0) {
+ cc33xx_error("PROBE request template failed");
+ goto out_free;
+ }
+
+ cc33xx_dump(DEBUG_SCAN, "SCAN: ", cmd, alloc_size);
+
+ ret = cc33xx_cmd_send(cc, CMD_SCAN, cmd, alloc_size, 0);
+ if (ret < 0) {
+ cc33xx_error("SCAN failed");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd_channels);
+ kfree(cmd);
+
+out_ssid_free:
+ kfree(ssid_list);
+
+ return ret;
+}
+
+static int __cc33xx_scan_stop(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif, u8 scan_type)
+{
+ struct cc33xx_cmd_scan_stop *stop;
+ int ret;
+
+ cc33xx_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+ stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+ if (!stop) {
+ cc33xx_error("failed to alloc memory to send sched scan stop");
+ return -ENOMEM;
+ }
+
+ stop->role_id = wlvif->role_id;
+ stop->scan_type = scan_type;
+
+ ret = cc33xx_cmd_send(cc, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
+ if (ret < 0) {
+ cc33xx_error("failed to send sched scan stop command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(stop);
+ return ret;
+}
+
+void cc33xx_scan_sched_scan_stop(struct cc33xx *cc,
+ struct cc33xx_vif *wlvif)
+{
+ __cc33xx_scan_stop(cc, wlvif, SCAN_REQUEST_CONNECT_PERIODIC_SCAN);
+}
+
+static int cc33xx_scan_start(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct cfg80211_scan_request *req)
+{
+ return cc33xx_scan_send(cc, wlvif, req);
+}
+
+int cc33xx_scan_stop(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ return __cc33xx_scan_stop(cc, wlvif, SCAN_REQUEST_ONE_SHOT);
+}
+
+void cc33xx_scan_complete_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct cc33xx *cc;
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+
+ dwork = to_delayed_work(work);
+ cc = container_of(dwork, struct cc33xx, scan_complete_work);
+
+ cc33xx_debug(DEBUG_SCAN, "Scanning complete");
+
+ mutex_lock(&cc->mutex);
+
+ if (unlikely(cc->state != CC33XX_STATE_ON))
+ goto out;
+
+ if (cc->scan.state == CC33XX_SCAN_STATE_IDLE)
+ goto out;
+
+ /* Rearm the tx watchdog just before idling scan. This
+ * prevents just-finished scans from triggering the watchdog
+ */
+ cc33xx_rearm_tx_watchdog_locked(cc);
+
+ cc->scan.state = CC33XX_SCAN_STATE_IDLE;
+ memset(cc->scan.scanned_ch, 0, sizeof(cc->scan.scanned_ch));
+ cc->scan.req = NULL;
+ cc->scan_wlvif = NULL;
+
+ if (cc->scan.failed) {
+ cc33xx_info("Scan completed due to error.");
+ cc33xx_queue_recovery_work(cc);
+ }
+
+ cc33xx_cmd_regdomain_config_locked(cc);
+
+ ieee80211_scan_completed(cc->hw, &info);
+
+out:
+ mutex_unlock(&cc->mutex);
+}
+
+int cc33xx_scan(struct cc33xx *cc, struct ieee80211_vif *vif, const u8 *ssid,
+ size_t ssid_len, struct cfg80211_scan_request *req)
+{
+ struct cc33xx_vif *wlvif = cc33xx_vif_to_data(vif);
+
+ if (cc->scan.state != CC33XX_SCAN_STATE_IDLE)
+ return -EBUSY;
+
+ cc->scan.state = CC33XX_SCAN_STATE_2GHZ_ACTIVE;
+
+ if (ssid_len && ssid) {
+ cc->scan.ssid_len = ssid_len;
+ memcpy(cc->scan.ssid, ssid, ssid_len);
+ } else {
+ cc->scan.ssid_len = 0;
+ }
+
+ cc->scan_wlvif = wlvif;
+ cc->scan.req = req;
+ memset(cc->scan.scanned_ch, 0, sizeof(cc->scan.scanned_ch));
+
+ /* we assume failure so that timeout scenarios are handled correctly */
+ cc->scan.failed = true;
+ ieee80211_queue_delayed_work(cc->hw, &cc->scan_complete_work,
+ msecs_to_jiffies(CC33XX_SCAN_TIMEOUT));
+
+ cc33xx_scan_start(cc, wlvif, req);
+
+ return 0;
+}
+
+inline void cc33xx_scan_sched_scan_results(struct cc33xx *cc)
+{
+ cc33xx_debug(DEBUG_SCAN, "got periodic scan results");
+
+ ieee80211_sched_scan_results(cc->hw);
+}
+
+void cc33xx_scan_completed(struct cc33xx *cc, struct cc33xx_vif *wlvif)
+{
+ cc->scan.failed = false;
+ cancel_delayed_work(&cc->scan_complete_work);
+ ieee80211_queue_delayed_work(cc->hw, &cc->scan_complete_work,
+ msecs_to_jiffies(0));
+}
diff --git a/drivers/net/wireless/ti/cc33xx/scan.h b/drivers/net/wireless/ti/cc33xx/scan.h
new file mode 100644
index 000000000000..8475008a8e97
--- /dev/null
+++ b/drivers/net/wireless/ti/cc33xx/scan.h
@@ -0,0 +1,364 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#ifndef __SCAN_H__
+#define __SCAN_H__
+
+#include "cc33xx.h"
+
+#define CC33XX_SCAN_TIMEOUT 30000 /* msec */
+
+enum {
+ CC33XX_SCAN_STATE_IDLE,
+ CC33XX_SCAN_STATE_2GHZ_ACTIVE,
+ CC33XX_SCAN_STATE_2GHZ_PASSIVE,
+ CC33XX_SCAN_STATE_5GHZ_ACTIVE,
+ CC33XX_SCAN_STATE_5GHZ_PASSIVE,
+ CC33XX_SCAN_STATE_DONE
+};
+
+struct conn_scan_ch_params {
+ __le16 min_duration;
+ __le16 max_duration;
+ __le16 passive_duration;
+
+ u8 channel;
+ u8 tx_power_att;
+
+ /* bit 0: DFS channel; bit 1: DFS enabled */
+ u8 flags;
+
+ u8 padding[3];
+} __packed;
+
+enum {
+ SCAN_SSID_TYPE_PUBLIC = 0,
+ SCAN_SSID_TYPE_HIDDEN = 1,
+};
+
+#define MAX_CHANNELS_2GHZ 14
+#define MAX_CHANNELS_4GHZ 4
+#define MAX_CHANNELS_5GHZ 32
+
+#define SCAN_MAX_CYCLE_INTERVALS 16
+#define SCAN_MAX_BANDS 3
+#define SCHED_SCAN_MAX_SSIDS 16
+
+/******************************************************************************
+ * ** *** *** ** *
+ * ** *** SCAN API *** ** *
+ * ** *** *** ** *
+ ******************************************************************************/
+
+#define CONN_SCAN_MAX_BAND (2)
+#define CONN_SCAN_MAX_CHANNELS_ALL_BANDS (46)
+#define SCAN_MAX_SCHED_SCAN_PLANS (12)
+
+enum scan_request_type {
+ SCAN_REQUEST_NONE,
+ SCAN_REQUEST_CONNECT_PERIODIC_SCAN,
+ SCAN_REQUEST_ONE_SHOT,
+ SCAN_REQUEST_SURVEY_SCAN,
+ SCAN_NUM_OF_REQUEST_TYPE
+};
+
+/******************************************************************************
+ * ID: CMD_SCAN
+ * Desc: This command will start scan process depending scan request
+ * type
+ * Return: CMD_COMPLETE
+ *****************************************************************************/
+/* struct cc33xx_ssid - SSIDs connection scan description
+ *
+ * @type: SSID type - SCAN_SSID_TYPE_HIDDEN/SCAN_SSID_TYPE_PUBLIC
+ *
+ * @len: Length of the ssid
+ *
+ * @ssid: SSID
+ */
+struct cc33xx_ssid {
+ u8 type;
+ u8 len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 padding[2];
+} __packed;
+
+/**
+ * struct cc33xx_cmd_ssid_list - scan SSID list description
+ *
+ * @role_id: roleID
+ *
+ * @num_of_ssids: Number of SSID in the list. MAX 16 entries
+ *
+ * @ssid_list: SSIDs to scan for (active scan only)
+ */
+struct cc33xx_cmd_ssid_list {
+ struct cc33xx_cmd_header header;
+
+ u8 role_id;
+ u8 scan_type;
+ u8 n_ssids;
+ struct cc33xx_ssid ssids[SCHED_SCAN_MAX_SSIDS];
+ u8 padding;
+} __packed;
+
+/**
+ * struct conn_scan_dwell_info - Channels duration info per band
+ *
+ * @min_duration: Min duration (in ms)
+ *
+ * @max_duration: Max duration (in ms)
+ *
+ * @passive_duration: Duration to use for passive scans (in ms)
+ */
+struct conn_scan_dwell_info {
+ __le16 min_duration;
+ __le16 max_duration;
+ __le16 passive_duration;
+} __packed;
+
+/**
+ * struct conn_scan_ch_info - Channels info
+ *
+ * @channel: channel number (channel_e)
+ *
+ * @tx_power_att: TX power level in dbm
+ *
+ * @flags: 0 - DFS channel, 1 - DFS enabled (to be included in active scan)
+ */
+struct conn_scan_ch_info {
+ u8 channel;
+ u8 tx_power_att;
+ u8 flags;
+} __packed;
+
+/**
+ * struct scan_one_shot_info - ONE_SHOT scan param
+ *
+ * @passive: Number of passive scan channels in bands BG,A
+ *
+ * @active: Number of active scan channels in bands BG,A
+ *
+ * @dfs: Number of DFS channels in A band
+ *
+ * @channel_list: Channel list info
+ * BG band channels are set from place 0 and forward.
+ * A band channels are from CONN_SCAN_MAX_CHANNELS_BG and forward.
+ * 6Ghz band channels are from CONN_SCAN_MAX_CHANNELS_A_BG and forward.
+
+ * @dwell_info: Scan duration time info per band
+ *
+ * @reserved:
+ *
+ */
+struct scan_one_shot_info {
+ u8 passive[CONN_SCAN_MAX_BAND];
+ u8 active[CONN_SCAN_MAX_BAND];
+ u8 dfs;
+
+ struct conn_scan_ch_info channel_list[CONN_SCAN_MAX_CHANNELS_ALL_BANDS];
+ struct conn_scan_dwell_info dwell_info[CONN_SCAN_MAX_BAND];
+ u8 reserved;
+};
+
+/**
+ * sched_scan_plans - Scan plans for scheduled scan
+ *
+ * Each scan plan consists of the number of iterations to scan and the
+ * interval between scans. When a scan plan finishes (i.e., it was run
+ * for the specified number of iterations), the next scan plan is
+ * executed. The scan plans are executed in the order they appear in
+ * the array (lower index first). The last scan plan will run infinitely
+ * (until requested to stop), thus must not specify the number of
+ * iterations. All other scan plans must specify the number of
+ * iterations.
+ */
+struct sched_scan_plan_cmd {
+ u32 interval; /* In seconds */
+ u32 iterations; /* Zero to run infinitely */
+};
+
+/* struct periodicScanParams_t - Periodic scan param
+ *
+ * @sched_scan_plans: Scan plans for a scheduled scan (defined in supplicant's driver.h)
+ * interval and iterations
+ *
+ * @sched_scan_plans_num:Number of scan plans in sched_scan_plans array
+ *
+ * @passive: Number of passive scan channels in bands BG,A
+ *
+ * @active: Number of active scan channels in bands BG,A
+ *
+ * @dfs: number of DFS channels in A band
+ *
+ * @channel_list: Channel list info.
+ * BG band channels are set from place 0 and forward.
+ * A band channels are set from CONN_SCAN_MAX_CHANNELS_BG and forward.
+ * 6Ghz band are set from CONN_SCAN_MAX_CHANNELS_A_BG and forward.
+ *
+ * @dwell_info: Scan duration time info per band
+ *
+ */
+struct scan_periodic_info {
+ struct sched_scan_plan_cmd sched_scan_plans[SCAN_MAX_SCHED_SCAN_PLANS];
+ u16 sched_scan_plans_num;
+
+ u8 passive[CONN_SCAN_MAX_BAND];
+ u8 active[CONN_SCAN_MAX_BAND];
+ u8 dfs;
+
+ struct conn_scan_ch_info channel_list[CONN_SCAN_MAX_CHANNELS_ALL_BANDS];
+ struct conn_scan_dwell_info dwell_info[CONN_SCAN_MAX_BAND];
+} __packed;
+
+/**
+ * struct scan_param - union for ONE_SHOT/PERIODIC scan param
+ *
+ * @one_shot: ONE_SHOT scan param
+ *
+ * @periodic: Periodic scan param
+ */
+struct scan_param {
+ union {
+ struct scan_one_shot_info one_shot;
+ struct scan_periodic_info periodic;
+ } u;
+} __packed;
+
+/**
+ * struct cc33xx_cmd_scan_params - scan configured param
+ *
+ * @scan_type: ONE_SHOT/PERIODIC scan
+ *
+ * @role_id: role ID
+ *
+ * @params: Scan parameter for ONE_SHOT/PERIODIC Scan
+ *
+ * @rssi_threshold: RSSI threshold for basic filter
+ *
+ * @snr_threshold: SNR threshold for basic filter
+ *
+ * @bssid: BSSID to scan for
+ *
+ * @ssid_from_list: 0 - if there are more than 5 SSIDs entries
+ * (list was sent SSID CONFIGURE COMMAND),
+ * 1 - 5 or less SSIDs entries, the list is at the end of the scan command
+ *
+ * @filter: 0 - not using filter and all the beacons/probe response frame
+ * forward to upper mac, 1 - using filter
+ *
+ * @num_of_ssids: Number of SSIDs
+ */
+struct cc33xx_cmd_scan_params {
+ struct cc33xx_cmd_header header;
+ u8 scan_type;
+ u8 role_id;
+
+ struct scan_param params;
+ s8 rssi_threshold; /* for filtering (in dBm) */
+ s8 snr_threshold; /* for filtering (in dB) */
+
+ u8 bssid[ETH_ALEN];
+ u8 padding[2];
+
+ u8 ssid_from_list; /* use ssid from configured ssid list */
+ u8 filter; /* forward only results with matching ssids */
+
+ u8 num_of_ssids;
+} __packed;
+
+/******************************************************************************
+ * ID: CMD_SET_PROBE_IE
+ * Desc: This command will set the Info elements data for
+ * probe request
+ * Return: CMD_COMPLETE
+ *******************************************************************************/
+#define MAX_EXTRA_IES_LEN 512
+/**
+ * struct cc33xx_cmd_set_ies - Probe request info elements
+ *
+ * @scan_type: ONE_SHOT/PERIODIC scan
+ *
+ * @role_id: roleID
+ *
+ * @data: info element buffer
+ *
+ * @len: info element length
+ */
+struct cc33xx_cmd_set_ies {
+ struct cc33xx_cmd_header header;
+ u8 scan_type;
+ u8 role_id;
+ __le16 len;
+ u8 data[MAX_EXTRA_IES_LEN];
+} __packed;
+
+/******************************************************************************
+ * ID: CMD_STOP_SCAN
+ * Desc: This command will stop scan process depending scan request
+ * type, and if early termination is on
+ * Return: CMD_COMPLETE
+ ******************************************************************************/
+/**
+ * struct cc33xx_cmd_scan_stop - scan stop param
+ *
+ * @scan_type: Scan request type
+ *
+ * @role_id: role ID
+ *
+ * @is_ET: TRUE - Early termination is on, FALSE - no ET
+ */
+struct cc33xx_cmd_scan_stop {
+ struct cc33xx_cmd_header header;
+
+ u8 scan_type;
+ u8 role_id;
+ u8 is_ET;
+ u8 padding;
+} __packed;
+
+int cc33xx_scan_stop(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+void cc33xx_scan_completed(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+int cc33xx_sched_scan_start(struct cc33xx *cc, struct cc33xx_vif *wlvif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies);
+void cc33xx_scan_sched_scan_stop(struct cc33xx *cc, struct cc33xx_vif *wlvif);
+
+int cc33xx_scan(struct cc33xx *cc, struct ieee80211_vif *vif,
+ const u8 *ssid, size_t ssid_len,
+ struct cfg80211_scan_request *req);
+void cc33xx_scan_complete_work(struct work_struct *work);
+void cc33xx_scan_sched_scan_results(struct cc33xx *cc);
+
+enum {
+ SCAN_SSID_FILTER_ANY = 0,
+ SCAN_SSID_FILTER_SPECIFIC = 1,
+ SCAN_SSID_FILTER_LIST = 2,
+ SCAN_SSID_FILTER_DISABLED = 3
+};
+
+#define SCAN_CHANNEL_FLAGS_DFS BIT(0) /* channel is passive until an
+ * activity is detected on it
+ */
+#define SCAN_CHANNEL_FLAGS_DFS_ENABLED BIT(1)
+
+struct cc33xx_scan_channels {
+ u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+ u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */
+ u8 dfs; /* number of dfs channels in 5ghz */
+ u8 passive_active; /* number of passive before active channels 2.4ghz */
+
+ struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+ struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
+ struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+};
+
+enum {
+ SCAN_TYPE_SEARCH = 0,
+ SCAN_TYPE_PERIODIC = 1,
+ SCAN_TYPE_TRACKING = 2,
+};
+
+#endif /* __CC33XX_SCAN_H__ */
--
2.25.1


2024-05-22 08:54:36

by Breno Leitao

[permalink] [raw]
Subject: Re: [PATCH 07/17] Add boot.c, boot.h

Hello Michael,

On Tue, May 21, 2024 at 08:18:31PM +0300, [email protected] wrote:
> From: Michael Nemanov <[email protected]>

> +static u8 *fetch_container(struct cc33xx *cc, const char *container_name,
> + size_t *container_len)
> +{
> + u8 *container_data = NULL;
> + const struct firmware *container;
> + int ret;
> +
> + ret = request_firmware(&container, container_name, cc->dev);
> +
> + if (ret < 0) {
> + cc33xx_error("could not get container %s: (%d)",
> + container_name, ret);
> + return NULL;
> + }
> +
> + if (container->size % 4) {
> + cc33xx_error("container size is not word-aligned: %zu",
> + container->size);
> + goto out;
> + }
> +
> + *container_len = container->size;
> + container_data = vmalloc(container->size);


I got the following error when compiling it:


drivers/net/wireless/ti/cc33xx/boot.c: In function ‘fetch_container’:
drivers/net/wireless/ti/cc33xx/boot.c:76:26: error: implicit declaration of function ‘vmalloc’; did you mean ‘kmalloc’? [-Werror=implicit-function-declaration]
76 | container_data = vmalloc(container->size);
| ^~~~~~~
| kmalloc
drivers/net/wireless/ti/cc33xx/boot.c:76:24: warning: assignment to ‘u8 *’ {aka ‘unsigned char *’} from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
76 | container_data = vmalloc(container->size);


2024-05-22 09:35:17

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 17/17] Add ti,cc33xx.yaml

On 21/05/2024 19:18, [email protected] wrote:
> From: Michael Nemanov <[email protected]>

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters

Missing commit msg.

Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.

Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline), work on fork of kernel
(don't, instead use mainline) or you ignore some maintainers (really
don't). Just use b4 and everything should be fine, although remember
about `b4 prep --auto-to-cc` if you added new patches to the patchset.

You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time, thus I will skip this patch entirely till you follow
the process allowing the patch to be tested.

Please kindly resend and include all necessary To/Cc entries.

Best regards,
Krzysztof


2024-05-22 09:39:05

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 01/17] Add cc33xx.h, cc33xx_i.h

On 21/05/2024 19:18, [email protected] wrote:
> From: Michael Nemanov <[email protected]>
>
> These are header files with definitions common to the entire driver.

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters


>
> Signed-off-by: Michael Nemanov <[email protected]>
> ---
> drivers/net/wireless/ti/cc33xx/cc33xx.h | 481 ++++++++++++++++++++++
> drivers/net/wireless/ti/cc33xx/cc33xx_i.h | 459 +++++++++++++++++++++
> 2 files changed, 940 insertions(+)
> create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx.h
> create mode 100644 drivers/net/wireless/ti/cc33xx/cc33xx_i.h
>
> diff --git a/drivers/net/wireless/ti/cc33xx/cc33xx.h b/drivers/net/wireless/ti/cc33xx/cc33xx.h
> new file mode 100644
> index 000000000000..3a2e56af4da7
> --- /dev/null
> +++ b/drivers/net/wireless/ti/cc33xx/cc33xx.h
> @@ -0,0 +1,481 @@
> +/* SPDX-License-Identifier: GPL-2.0-only
> + *
> + * Copyright (C) 2022-2024 Texas Instruments Incorporated - https://www.ti.com/
> + */
> +
> +#ifndef __CC33XX_H__
> +#define __CC33XX_H__
> +
> +#include "cc33xx_i.h"
> +#include "rx.h"
> +
> +/* Wireless Driver Version */
> +#define DRV_VERSION "1.7.0.108"
> +
> +/* The maximum number of Tx descriptors in all chip families */
> +#define CC33XX_MAX_TX_DESCRIPTORS 32
> +
> +#define CC33XX_CMD_MAX_SIZE (896)
> +#define CC33XX_INI_PARAM_COMMAND_SIZE (16UL)
> +#define CC33XX_INI_CMD_MAX_SIZE (CC33X_CONF_SIZE + CC33XX_INI_PARAM_COMMAND_SIZE + sizeof(int))
> +
> +#define CC33XX_CMD_BUFFER_SIZE ((CC33XX_INI_CMD_MAX_SIZE > CC33XX_CMD_MAX_SIZE)\
> + ? CC33XX_INI_CMD_MAX_SIZE : CC33XX_CMD_MAX_SIZE)
> +
> +#define CC33XX_NUM_MAC_ADDRESSES 3
> +
> +#define CC33XX_AGGR_BUFFER_SIZE (8 * PAGE_SIZE)
> +
> +#define CC33XX_NUM_TX_DESCRIPTORS 32
> +#define CC33XX_NUM_RX_DESCRIPTORS 32
> +
> +#define CC33XX_RX_BA_MAX_SESSIONS 13
> +
> +#define CC33XX_MAX_AP_STATIONS 16
> +
> +struct cc33xx_tx_hw_descr;
> +struct cc33xx_rx_descriptor;
> +struct partial_rx_frame;
> +struct core_fw_status;
> +struct core_status;
> +
> +enum wl_rx_buf_align;
> +
> +struct driver_fw_versions {
> + const char *driver_ver;
> + struct cc33xx_acx_fw_versions *fw_ver;
> +};
> +
> +struct cc33xx_stats {
> + void *fw_stats;
> + unsigned long fw_stats_update;
> + unsigned int retry_count;
> + unsigned int excessive_retries;
> +};
> +
> +struct cc33xx_ant_diversity {
> + u8 diversity_enable;
> + s8 rssi_threshold;
> + u8 default_antenna;
> + u8 padding;
> +};
> +
> +struct cc33xx {
> + bool initialized;
> + struct ieee80211_hw *hw;
> + bool mac80211_registered;
> +
> + struct device *dev;
> + struct platform_device *pdev;
> +
> + struct cc33xx_if_operations *if_ops;
> +
> + int wakeirq;
> +
> + spinlock_t cc_lock; /* Protects critical sections */

Which ones. Your comment is entirely useless, just to satisfy checkpatch.

> +
> + enum cc33xx_state state;
> + bool plt;
> + enum plt_mode plt_mode;
> + u8 plt_role_id;
> + u8 fem_manuf;
> + u8 last_vif_count;
> + struct mutex mutex; /* Protect all CC33xx operations */

?!? So double lock?

> + struct core_status *core_status;
> + u8 last_fw_rls_idx;
> + u8 command_result[CC33XX_CMD_MAX_SIZE];
> + u16 result_length;
> + struct partial_rx_frame partial_rx;
> +
> + unsigned long flags;
> +
> + void *nvs_mac_addr;
> + size_t nvs_mac_addr_len;
> + struct cc33xx_fw_download *fw_download;
> +
> + struct mac_address addresses[CC33XX_NUM_MAC_ADDRESSES];
> +
> + unsigned long links_map[BITS_TO_LONGS(CC33XX_MAX_LINKS)];
> + unsigned long roles_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)];
> + unsigned long roc_map[BITS_TO_LONGS(CC33XX_MAX_ROLES)];
> + unsigned long rate_policies_map[BITS_TO_LONGS(CC33XX_MAX_RATE_POLICIES)];
> +
> + u8 session_ids[CC33XX_MAX_LINKS];
> +
> + struct list_head wlvif_list;
> +
> + u8 sta_count;
> + u8 ap_count;
> +
> + struct cc33xx_acx_mem_map *target_mem_map;
> +
> + /* Accounting for allocated / available TX blocks on HW */
> +
> + u32 tx_blocks_available;
> + u32 tx_allocated_blocks;
> +
> + /* Accounting for allocated / available Tx packets in HW */
> +
> + u32 tx_allocated_pkts[NUM_TX_QUEUES];
> +
> + /* Time-offset between host and chipset clocks */
> +
> + /* Frames scheduled for transmission, not handled yet */
> + int tx_queue_count[NUM_TX_QUEUES];
> + unsigned long queue_stop_reasons[NUM_TX_QUEUES * CC33XX_NUM_MAC_ADDRESSES];
> +
> + /* Frames received, not handled yet by mac80211 */
> + struct sk_buff_head deferred_rx_queue;
> +
> + /* Frames sent, not returned yet to mac80211 */
> + struct sk_buff_head deferred_tx_queue;
> +
> + struct work_struct tx_work;
> + struct workqueue_struct *freezable_wq;
> +
> + /*freezable wq for netstack_work*/
> + struct workqueue_struct *freezable_netstack_wq;
> +
> + /* Pending TX frames */
> + unsigned long tx_frames_map[BITS_TO_LONGS(CC33XX_MAX_TX_DESCRIPTORS)];
> + struct sk_buff *tx_frames[CC33XX_MAX_TX_DESCRIPTORS];
> + int tx_frames_cnt;
> +
> + /* FW Rx counter */
> + u32 rx_counter;
> +
> + /* Intermediate buffer, used for packet aggregation */
> + u8 *aggr_buf;
> + u32 aggr_buf_size;
> + size_t max_transaction_len;
> +
> + /* Reusable dummy packet template */
> + struct sk_buff *dummy_packet;
> +
> + /* Network stack work */
> + struct work_struct netstack_work;
> + /* FW log buffer */
> + u8 *fwlog;
> +
> + /* Number of valid bytes in the FW log buffer */
> + ssize_t fwlog_size;
> +
> + /* Hardware recovery work */
> + struct work_struct recovery_work;
> +
> + struct work_struct irq_deferred_work;
> +
> + /* Reg domain last configuration */
> + DECLARE_BITMAP(reg_ch_conf_last, 64);
> + /* Reg domain pending configuration */
> + DECLARE_BITMAP(reg_ch_conf_pending, 64);
> +
> + /* Lock-less list for deferred event handling */
> + struct llist_head event_list;
> + /* The mbox event mask */
> + u32 event_mask;
> + /* events to unmask only when ap interface is up */
> + u32 ap_event_mask;
> +
> + /* Are we currently scanning */
> + struct cc33xx_vif *scan_wlvif;
> + struct cc33xx_scan scan;
> + struct delayed_work scan_complete_work;
> +
> + struct ieee80211_vif *roc_vif;
> + struct delayed_work roc_complete_work;
> +
> + struct cc33xx_vif *sched_vif;
> +
> + u8 mac80211_scan_stopped;
> +
> + /* The current band */
> + enum nl80211_band band;
> +
> + /* in dBm */
> + int power_level;
> +
> + struct cc33xx_stats stats;
> +
> + __le32 *buffer_32;
> +
> + /* Current chipset configuration */
> + struct cc33xx_conf_file conf;
> +
> + bool enable_11a;
> +
> + /* bands supported by this instance of cc33xx */
> + struct ieee80211_supported_band bands[CC33XX_NUM_BANDS];
> +
> + /* wowlan trigger was configured during suspend.
> + * (currently, only "ANY" and "PATTERN" trigger is supported)
> + */
> +
> + bool keep_device_power;
> +
> + /* AP-mode - links indexed by HLID. The global and broadcast links
> + * are always active.
> + */
> + struct cc33xx_link links[CC33XX_MAX_LINKS];
> +
> + /* number of currently active links */
> + int active_link_count;
> +
> + /* AP-mode - a bitmap of links currently in PS mode according to FW */
> + unsigned long ap_fw_ps_map;
> +
> + /* AP-mode - a bitmap of links currently in PS mode in mac80211 */
> + unsigned long ap_ps_map;
> +
> + /* Quirks of specific hardware revisions */
> + unsigned int quirks;
> +
> + /* number of currently active RX BA sessions */
> + int ba_rx_session_count;
> +
> + /* AP-mode - number of currently connected stations */
> + int active_sta_count;
> +
> + /* last wlvif we transmitted from */
> + struct cc33xx_vif *last_wlvif;
> +
> + /* work to fire when Tx is stuck */
> + struct delayed_work tx_watchdog_work;
> +
> + /* HW HT (11n) capabilities */
> + struct ieee80211_sta_ht_cap ht_cap[CC33XX_NUM_BANDS];
> +
> + /* the current dfs region */
> + enum nl80211_dfs_regions dfs_region;
> + bool radar_debug_mode;
> +
> + /* RX Data filter rule state - enabled/disabled */
> + /* used in CONFIG PM AND W8 Code */
> + unsigned long rx_filter_enabled[BITS_TO_LONGS(CC33XX_MAX_RX_FILTERS)];
> +
> + /* mutex for protecting the tx_flush function */
> + struct mutex flush_mutex;
> +
> + /* sleep auth value currently configured to FW */
> + int sleep_auth;
> +
> + /*ble_enable value - 1=enabled, 0=disabled. */
> + int ble_enable;
> +
> + /* parameters for joining a TWT agreement */
> + int min_wake_duration_usec;
> + int min_wake_interval_mantissa;
> + int min_wake_interval_exponent;
> + int max_wake_interval_mantissa;
> + int max_wake_interval_exponent;
> +
> + /* the number of allocated MAC addresses in this chip */
> + int num_mac_addr;
> +
> + /* sta role index - if 0 - wlan0 primary station interface,
> + * if 1 - wlan2 - secondary station interface
> + */
> + u8 sta_role_idx;
> +
> + u16 max_cmd_size;
> +
> + struct completion nvs_loading_complete;
> + struct completion command_complete;
> +
> + /* dynamic fw traces */
> + u32 dynamic_fw_traces;
> +
> + /* buffer for sending commands to FW */
> + u8 cmd_buf[CC33XX_CMD_BUFFER_SIZE];
> +
> + /* number of keys requiring extra spare mem-blocks */
> + int extra_spare_key_count;

This entire struct is quite unmanageable...

> +
> + u8 efuse_mac_address[ETH_ALEN];
> +
> + u32 fuse_rom_structure_version;
> + u32 device_part_number;
> + u32 pg_version;
> + u8 disable_5g;
> + u8 disable_6g;

Please fix trivial whitespace issues.

This driver looks like having basic issues unresolved, really basic. I
advise to get internal TI review first, before you will be using
community resources for these trivial things.

Please also confirm that you also fixed all warnings from:
1. checkpatch --strict
2. smatch
3. sparse
4. coccinelle/coccicheck



Best regards,
Krzysztof


2024-05-22 11:17:37

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 07/17] Add boot.c, boot.h

On 22/05/2024 10:54, Breno Leitao wrote:
> Hello Michael,
>
> On Tue, May 21, 2024 at 08:18:31PM +0300, [email protected] wrote:
>> From: Michael Nemanov <[email protected]>
>
>> +static u8 *fetch_container(struct cc33xx *cc, const char *container_name,
>> + size_t *container_len)
>> +{
>> + u8 *container_data = NULL;
>> + const struct firmware *container;
>> + int ret;
>> +
>> + ret = request_firmware(&container, container_name, cc->dev);
>> +
>> + if (ret < 0) {
>> + cc33xx_error("could not get container %s: (%d)",
>> + container_name, ret);
>> + return NULL;
>> + }
>> +
>> + if (container->size % 4) {
>> + cc33xx_error("container size is not word-aligned: %zu",
>> + container->size);
>> + goto out;
>> + }
>> +
>> + *container_len = container->size;
>> + container_data = vmalloc(container->size);
>
>
> I got the following error when compiling it:
>
>
> drivers/net/wireless/ti/cc33xx/boot.c: In function ‘fetch_container’:
> drivers/net/wireless/ti/cc33xx/boot.c:76:26: error: implicit declaration of function ‘vmalloc’; did you mean ‘kmalloc’? [-Werror=implicit-function-declaration]
> 76 | container_data = vmalloc(container->size);
> | ^~~~~~~

So neither this nor previous version was ever built...

Best regards,
Krzysztof


2024-05-22 15:16:01

by Nemanov, Michael

[permalink] [raw]
Subject: Re: [EXTERNAL] Re: [PATCH 07/17] Add boot.c, boot.h

On 5/22/2024 2:12 PM, Krzysztof Kozlowski wrote:
> So neither this nor previous version was ever built...

I was building with ARCH=arm. Seems like linux/vmalloc.h is included
automatically with this architecture but not others. I'll fix the
include and make sure to test with other archs as well.

Michael.


2024-05-22 15:44:57

by Nemanov, Michael

[permalink] [raw]
Subject: Re: [PATCH 01/17] Add cc33xx.h, cc33xx_i.h

On 5/22/2024 12:38 PM, Krzysztof Kozlowski wrote:
> Please also confirm that you also fixed all warnings from:
> 1. checkpatch --strict
> 2. smatch
> 3. sparse
> 4. coccinelle/coccicheck

I was running checkpatch --strict and sparse. I'll add smatch and
coccinelle/coccicheck to the test list as well as testing on other
architectures.

Thanks and regards, Michael.


2024-05-23 07:15:41

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 00/17] wifi: cc33xx: Add driver for new TI CC33xx wireless device family

<[email protected]> writes:

> From: Michael Nemanov <[email protected]>
>
> Hello everyone,
>
> This series adds support for CC33xx which is a new family of WLAN
> IEEE802.11 a/b/g/n/ax and BLE 5.4 transceivers by Texas Instruments.
> These devices are 20MHz single spatial stream enabling STA
> (IEEE802.11ax) and AP (IEEE802.11n only) roles as well as both roles
> simultaneously. Communication to the CC33xx is done via 4-bit SDIO
> with two extra GPIOs: Enable and Interrupt.
>
> Data sheet: https://www.ti.com/lit/gpn/cc3301
>
> This driver's architecture is a soft-MAC and derivative of existing
> wl18xx + wlcore code [1]. It has been tested with the AM335x, AM625x,
> and i.MX8-MP evaluation kits.
>
> All code passes sparse and checkpatch with very few pragmatic exceptions.
>
> Known gaps to be addressed in following patches:
> 1. BLE support
>
> Test log:
> https://0x0.st/XPUd.log
>
> Change log:
> v1:
> * Added dt-bindings
> * Removed debugfs to ease review
> * Fix build issue with CONFIG_CFG80211_CERTIFICATION_ONUS
> * Fix multiple build warnings found with Clang 18 and W=12
>
> RFC: https://lore.kernel.org/linux-wireless/[email protected]/
>
>
> [1] It was considered implementing CC33xx as another user of wlcore
> but The differences in HW, host interface, IRQ functionality, Rx/Tx
> behavior and supported features were too significant so this was
> abandoned.

The community (myself included) has been frustrated that TI has dropped
the ball on their existing wireless drivers:

https://docs.kernel.org/process/maintainers.html#ti-wilink-wireless-drivers

This kind of behaviour is not exactly building trust. So how is this
driver going to be any different?

--
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2024-05-24 07:49:11

by Nemanov, Michael

[permalink] [raw]
Subject: Re: [PATCH 00/17] wifi: cc33xx: Add driver for new TI CC33xx wireless device family


On 5/23/2024 10:15 AM, Kalle Valo wrote:
> The community (myself included) has been frustrated that TI has dropped
> the ball on their existing wireless drivers:
>
> https://docs.kernel.org/process/maintainers.html#ti-wilink-wireless-drivers
>
> This kind of behaviour is not exactly building trust. So how is this
> driver going to be any different?

I understand the frustration. As the engineer leading the development of
the CC33xx driver I can say that CC3300 and CC3301 are the first in a
family whose roadmap goes beyond 2030. I can also say that Linux driver
support is foundational for this line of products and TI is committed to
supporting these devices in the linux-wireless community for at least 10
years. Unlike previous drivers, development and maintenance of CC33xx
will be done by TI engineers and not a 3rd party. Is this acceptable?

Michael.


2024-05-26 06:36:25

by Nemanov, Michael

[permalink] [raw]
Subject: Re: [PATCH 17/17] Add ti,cc33xx.yaml


On 5/22/2024 12:35 PM, Krzysztof Kozlowski wrote:
> Missing commit msg.
>
> Please use scripts/get_maintainers.pl to get a list of necessary people
> and lists to CC. It might happen, that command when run on an older
> kernel, gives you outdated entries. Therefore please be sure you base
> your patches on recent Linux kernel.
>
> Tools like b4 or scripts/get_maintainer.pl provide you proper list of
> people, so fix your workflow. Tools might also fail if you work on some
> ancient tree (don't, instead use mainline), work on fork of kernel
> (don't, instead use mainline) or you ignore some maintainers (really
> don't). Just use b4 and everything should be fine, although remember
> about `b4 prep --auto-to-cc` if you added new patches to the patchset.

I'm working on mainline but was not using scripts/get_maintainers.pl
correctly. Will fix the address list for v2 along with proper names for
all patches.

Michael.