Project import generated by Copybara.
GitOrigin-RevId: a92375ebc2f1e14ddd0baf546636cf1edb5dac21
diff --git a/arch/arm/configs/ipq5018_sirocco_defconfig b/arch/arm/configs/ipq5018_sirocco_defconfig
index 1d1cb53..26ef7e2 100644
--- a/arch/arm/configs/ipq5018_sirocco_defconfig
+++ b/arch/arm/configs/ipq5018_sirocco_defconfig
@@ -4469,7 +4469,7 @@
# CONFIG_SQUASHFS_DECOMP_SINGLE is not set
# CONFIG_SQUASHFS_DECOMP_MULTI is not set
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
-# CONFIG_SQUASHFS_XATTR is not set
+CONFIG_SQUASHFS_XATTR=y
# CONFIG_SQUASHFS_ZLIB is not set
# CONFIG_SQUASHFS_LZ4 is not set
# CONFIG_SQUASHFS_LZO is not set
diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 33450fa..5b1387c 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -814,7 +814,7 @@
<0 309 1>,
<0 310 1>,
<0 311 1>,
- <0 312 1>,
+ <0 334 1>,
<0 313 1>, /* o_wcss_apps_intr[25] */
<0 314 1>,
@@ -840,7 +840,7 @@
<0 332 1>,
<0 333 1>,
- <0 334 1>,
+ <0 312 1>,
<0 335 1>,
<0 336 1>,
<0 337 1>,
diff --git a/arch/arm64/boot/dts/qcom/sirocco-p0.dts b/arch/arm64/boot/dts/qcom/sirocco-p0.dts
index 6909b0d..cd7ec02 100644
--- a/arch/arm64/boot/dts/qcom/sirocco-p0.dts
+++ b/arch/arm64/boot/dts/qcom/sirocco-p0.dts
@@ -421,6 +421,7 @@
&i2c_0 {
pinctrl-0 = <&i2c_pins>;
pinctrl-names = "default";
+ force-dma-mode;
status = "ok";
ina231: ina231@40 {
diff --git a/arch/arm64/configs/ipq5018_sirocco_defconfig b/arch/arm64/configs/ipq5018_sirocco_defconfig
index 21ae20c..99fcac2 100644
--- a/arch/arm64/configs/ipq5018_sirocco_defconfig
+++ b/arch/arm64/configs/ipq5018_sirocco_defconfig
@@ -4446,7 +4446,7 @@
# CONFIG_SQUASHFS_DECOMP_SINGLE is not set
# CONFIG_SQUASHFS_DECOMP_MULTI is not set
CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
-# CONFIG_SQUASHFS_XATTR is not set
+CONFIG_SQUASHFS_XATTR=y
# CONFIG_SQUASHFS_ZLIB is not set
# CONFIG_SQUASHFS_LZ4 is not set
# CONFIG_SQUASHFS_LZO is not set
diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c
index db6da21..74ad8bf 100644
--- a/drivers/hid/hid-bigbenff.c
+++ b/drivers/hid/hid-bigbenff.c
@@ -191,7 +191,7 @@
struct bigben_device, worker);
struct hid_field *report_field = bigben->report->field[0];
- if (bigben->removed)
+ if (bigben->removed || !report_field)
return;
if (bigben->work_led) {
diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c
index 3f0ed6a..e19e2b5 100644
--- a/drivers/hid/hid-chicony.c
+++ b/drivers/hid/hid-chicony.c
@@ -58,8 +58,12 @@
static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-
+ struct usb_interface *intf;
+
+ if (!hid_is_usb(hdev))
+ return rdesc;
+
+ intf = to_usb_interface(hdev->dev.parent);
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
/* Change usage maximum and logical maximum from 0x7fff to
* 0x2fff, so they don't exceed HID_MAX_USAGES */
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index 902a60e..8c895c8 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -553,7 +553,12 @@
int ret;
unsigned long quirks = id->driver_data;
struct corsair_drvdata *drvdata;
- struct usb_interface *usbif = to_usb_interface(dev->dev.parent);
+ struct usb_interface *usbif;
+
+ if (!hid_is_usb(dev))
+ return -EINVAL;
+
+ usbif = to_usb_interface(dev->dev.parent);
drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata),
GFP_KERNEL);
diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c
index dae1937..0e8f424 100644
--- a/drivers/hid/hid-elan.c
+++ b/drivers/hid/hid-elan.c
@@ -50,7 +50,7 @@
static int is_not_elan_touchpad(struct hid_device *hdev)
{
- if (hdev->bus == BUS_USB) {
+ if (hid_is_usb(hdev)) {
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
return (intf->altsetting->desc.bInterfaceNumber !=
diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c
index 0d22713..2876cb6 100644
--- a/drivers/hid/hid-elo.c
+++ b/drivers/hid/hid-elo.c
@@ -229,6 +229,9 @@
struct elo_priv *priv;
int ret;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/hid/hid-holtek-kbd.c b/drivers/hid/hid-holtek-kbd.c
index 0a38e8e..403506b 100644
--- a/drivers/hid/hid-holtek-kbd.c
+++ b/drivers/hid/hid-holtek-kbd.c
@@ -140,12 +140,17 @@
static int holtek_kbd_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
- int ret = hid_parse(hdev);
+ struct usb_interface *intf;
+ int ret;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
+ ret = hid_parse(hdev);
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ intf = to_usb_interface(hdev->dev.parent);
if (!ret && intf->cur_altsetting->desc.bInterfaceNumber == 1) {
struct hid_input *hidinput;
list_for_each_entry(hidinput, &hdev->inputs, list) {
diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c
index 195b735..b7172c4 100644
--- a/drivers/hid/hid-holtek-mouse.c
+++ b/drivers/hid/hid-holtek-mouse.c
@@ -62,6 +62,14 @@
return rdesc;
}
+static int holtek_mouse_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+ return 0;
+}
+
static const struct hid_device_id holtek_mouse_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) },
@@ -83,6 +91,7 @@
.name = "holtek_mouse",
.id_table = holtek_mouse_devices,
.report_fixup = holtek_mouse_report_fixup,
+ .probe = holtek_mouse_probe,
};
module_hid_driver(holtek_mouse_driver);
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 0dc7cdf..2c7e7c0 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -769,12 +769,18 @@
static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
- struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
- __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ struct usb_interface *iface;
+ __u8 iface_num;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct lg_drv_data *drv_data;
int ret;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
+ iface = to_usb_interface(hdev->dev.parent);
+ iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+
/* G29 only work with the 1st interface */
if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
(iface_num != 0)) {
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 54d811f..f3cdb19 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -1680,7 +1680,7 @@
case recvr_type_27mhz: no_dj_interfaces = 2; break;
case recvr_type_bluetooth: no_dj_interfaces = 2; break;
}
- if (hid_is_using_ll_driver(hdev, &usb_hid_driver)) {
+ if (hid_is_usb(hdev)) {
intf = to_usb_interface(hdev->dev.parent);
if (intf && intf->altsetting->desc.bInterfaceNumber >=
no_dj_interfaces) {
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index 2666af0..e4e9471 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -798,12 +798,18 @@
static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
- unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+ struct usb_interface *intf;
+ unsigned short ifnum;
unsigned long quirks = id->driver_data;
struct pk_device *pk;
struct pcmidi_snd *pm = NULL;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
+ intf = to_usb_interface(hdev->dev.parent);
+ ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
pk = kzalloc(sizeof(*pk), GFP_KERNEL);
if (pk == NULL) {
hid_err(hdev, "can't alloc descriptor\n");
diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c
index ffcd444..4b18e1a 100644
--- a/drivers/hid/hid-roccat-arvo.c
+++ b/drivers/hid/hid-roccat-arvo.c
@@ -344,6 +344,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c
index ce5f225..e95d59c 100644
--- a/drivers/hid/hid-roccat-isku.c
+++ b/drivers/hid/hid-roccat-isku.c
@@ -324,6 +324,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
index 509b9bb..6cf59b5 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -749,6 +749,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c
index 0316edf..1896c69 100644
--- a/drivers/hid/hid-roccat-koneplus.c
+++ b/drivers/hid/hid-roccat-koneplus.c
@@ -431,6 +431,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c
index 5248b3c..cf8eeb3 100644
--- a/drivers/hid/hid-roccat-konepure.c
+++ b/drivers/hid/hid-roccat-konepure.c
@@ -133,6 +133,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
index 9600128..6fb9b95 100644
--- a/drivers/hid/hid-roccat-kovaplus.c
+++ b/drivers/hid/hid-roccat-kovaplus.c
@@ -501,6 +501,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c
index 4a88a76..d5ddf0d 100644
--- a/drivers/hid/hid-roccat-lua.c
+++ b/drivers/hid/hid-roccat-lua.c
@@ -160,6 +160,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c
index 989927d..4fcc8e7 100644
--- a/drivers/hid/hid-roccat-pyra.c
+++ b/drivers/hid/hid-roccat-pyra.c
@@ -449,6 +449,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c
index 3956a6c..5bf1971 100644
--- a/drivers/hid/hid-roccat-ryos.c
+++ b/drivers/hid/hid-roccat-ryos.c
@@ -141,6 +141,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c
index 818701f..a784bb4 100644
--- a/drivers/hid/hid-roccat-savu.c
+++ b/drivers/hid/hid-roccat-savu.c
@@ -113,6 +113,9 @@
{
int retval;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
index 2e1c311..cf5992e 100644
--- a/drivers/hid/hid-samsung.c
+++ b/drivers/hid/hid-samsung.c
@@ -152,6 +152,9 @@
int ret;
unsigned int cmask = HID_CONNECT_DEFAULT;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
diff --git a/drivers/hid/hid-u2fzero.c b/drivers/hid/hid-u2fzero.c
index 95e0807..6f107e3 100644
--- a/drivers/hid/hid-u2fzero.c
+++ b/drivers/hid/hid-u2fzero.c
@@ -286,7 +286,7 @@
unsigned int minor;
int ret;
- if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
+ if (!hid_is_usb(hdev))
return -EINVAL;
dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL);
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index 8e9c9e6..4edb241 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -164,6 +164,9 @@
struct uclogic_drvdata *drvdata = NULL;
bool params_initialized = false;
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
+
/*
* libinput requires the pad interface to be on a different node
* than the pen, so use QUIRK_MULTI_INPUT for all tablets.
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index e80c812..ed4ede5 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -841,8 +841,7 @@
struct uclogic_params p = {0, };
/* Check arguments */
- if (params == NULL || hdev == NULL ||
- !hid_is_using_ll_driver(hdev, &usb_hid_driver)) {
+ if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {
rc = -EINVAL;
goto cleanup;
}
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index cd71e71..a54a177 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -726,7 +726,7 @@
* Skip the query for this type and modify defaults based on
* interface number.
*/
- if (features->type == WIRELESS) {
+ if (features->type == WIRELESS && intf) {
if (intf->cur_altsetting->desc.bInterfaceNumber == 0)
features->device_type = WACOM_DEVICETYPE_WL_MONITOR;
else
@@ -2185,7 +2185,7 @@
if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
char *product_name = wacom->hdev->name;
- if (hid_is_using_ll_driver(wacom->hdev, &usb_hid_driver)) {
+ if (hid_is_usb(wacom->hdev)) {
struct usb_interface *intf = to_usb_interface(wacom->hdev->dev.parent);
struct usb_device *dev = interface_to_usbdev(intf);
product_name = dev->product;
@@ -2416,6 +2416,9 @@
wacom_destroy_battery(wacom);
+ if (!usbdev)
+ return;
+
/* Stylus interface */
hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
wacom1 = hid_get_drvdata(hdev1);
@@ -2695,8 +2698,6 @@
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
- struct usb_device *dev = interface_to_usbdev(intf);
struct wacom *wacom;
struct wacom_wac *wacom_wac;
struct wacom_features *features;
@@ -2731,8 +2732,14 @@
wacom_wac->hid_data.inputmode = -1;
wacom_wac->mode_report = -1;
- wacom->usbdev = dev;
- wacom->intf = intf;
+ if (hid_is_usb(hdev)) {
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_device *dev = interface_to_usbdev(intf);
+
+ wacom->usbdev = dev;
+ wacom->intf = intf;
+ }
+
mutex_init(&wacom->lock);
INIT_DELAYED_WORK(&wacom->init_work, wacom_init_work);
INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 3417f7d..65831d9 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -280,6 +280,7 @@
void (*read_rx_fifo)(struct qup_i2c_dev *qup);
/* function to write tags in tx fifo for i2c read transfer */
void (*write_rx_tags)(struct qup_i2c_dev *qup);
+ bool force_dma;
};
static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
@@ -1529,8 +1530,8 @@
total_len += msgs[idx].len;
}
- if (!no_dma && qup->is_dma &&
- (total_len > qup->out_fifo_sz || total_len > qup->in_fifo_sz)) {
+ if (!no_dma && qup->is_dma && (qup->force_dma ||
+ (total_len > qup->out_fifo_sz || total_len > qup->in_fifo_sz))) {
qup->use_dma = true;
} else {
qup->blk.is_tx_blk_mode = max_tx_len > qup->out_fifo_sz -
@@ -1884,6 +1885,9 @@
qup->in_blk_sz, qup->in_fifo_sz,
qup->out_blk_sz, qup->out_fifo_sz);
+ qup->force_dma = of_property_read_bool(pdev->dev.of_node,
+ "force-dma-mode");
+
i2c_set_adapdata(&qup->adap, qup);
qup->adap.dev.parent = qup->dev;
qup->adap.dev.of_node = pdev->dev.of_node;
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 8a537fe..48784f8 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2107,8 +2107,6 @@
int err = 0;
size_t dma_buf_size = 0;
- dev = qdev;
-
sec_kobj = kobject_create_and_add("sec_key", NULL);
if (!sec_kobj) {
@@ -2329,8 +2327,6 @@
if (!buf) {
pr_err("Failed to allocate page\n");
return -ENOMEM;
- } else {
- printk("\n memory allocated at physical address 0x%x\n",dma_buf);
}
/*
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index b569b05..8be4c50 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -1653,32 +1653,32 @@
struct mmc_card *card = mq->card;
struct mmc_host *host = card->host;
blk_status_t error = BLK_STS_OK;
- int retries = 0;
do {
u32 status;
int err;
+ int retries = 0;
- mmc_blk_rw_rq_prep(mqrq, card, 1, mq);
+ while (retries++ <= MMC_READ_SINGLE_RETRIES) {
+ mmc_blk_rw_rq_prep(mqrq, card, 1, mq);
- mmc_wait_for_req(host, mrq);
+ mmc_wait_for_req(host, mrq);
- err = mmc_send_status(card, &status);
- if (err)
- goto error_exit;
-
- if (!mmc_host_is_spi(host) &&
- !mmc_blk_in_tran_state(status)) {
- err = mmc_blk_fix_state(card, req);
+ err = mmc_send_status(card, &status);
if (err)
goto error_exit;
+
+ if (!mmc_host_is_spi(host) &&
+ !mmc_blk_in_tran_state(status)) {
+ err = mmc_blk_fix_state(card, req);
+ if (err)
+ goto error_exit;
+ }
+
+ if (!mrq->cmd->error)
+ break;
}
- if (mrq->cmd->error && retries++ < MMC_READ_SINGLE_RETRIES)
- continue;
-
- retries = 0;
-
if (mrq->cmd->error ||
mrq->data->error ||
(!mmc_host_is_spi(host) &&
diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c
index 1bffe3f..1764069 100644
--- a/drivers/net/wireless/ath/ath10k/testmode.c
+++ b/drivers/net/wireless/ath/ath10k/testmode.c
@@ -89,7 +89,7 @@
goto out;
}
- cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC, false);
out:
spin_unlock_bh(&ar->data_lock);
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 11ed515..1415509 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -688,7 +688,7 @@
if (ret < 0)
return ret;
- vector = (i % num_vectors) + base_vector;
+ vector = (i % num_vectors);
if (i >= ATH11K_EXT_IRQ_GRP_NUM_MAX)
break;
diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c
index ce4a689..54c9b1b 100644
--- a/drivers/net/wireless/ath/ath11k/ce.c
+++ b/drivers/net/wireless/ath/ath11k/ce.c
@@ -431,6 +431,11 @@
ATH11K_MEMORY_STATS_DEC(ab, ce_rx_pipe, skb->truesize);
+ if (!ATH11K_SKB_RXCB(skb)->paddr) {
+ ath11k_warn(ab, "Invalid paddr in skb received in CE\n");
+ continue;
+ }
+
dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr,
max_nbytes, DMA_FROM_DEVICE);
@@ -512,6 +517,11 @@
if (!skb)
continue;
+ if (!ATH11K_SKB_CB(skb)->paddr) {
+ ath11k_warn(ab, "Invalid padd in Tx skb in CE\n");
+ continue;
+ }
+
dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, skb->len,
DMA_TO_DEVICE);
if ((!pipe->send_cb) || ab->hw_params.credit_flow) {
@@ -622,6 +632,7 @@
{
struct ath11k_ce_ring *ce_ring;
dma_addr_t base_addr;
+ unsigned long off;
ce_ring = kzalloc(struct_size(ce_ring, skb, nentries), GFP_KERNEL);
if (ce_ring == NULL)
@@ -650,12 +661,13 @@
ce_ring->base_addr_ce_space_unaligned = base_addr;
- ce_ring->base_addr_owner_space = PTR_ALIGN(
- ce_ring->base_addr_owner_space_unaligned,
+ ce_ring->base_addr_ce_space = (dma_addr_t) ALIGN(
+ (unsigned long)ce_ring->base_addr_ce_space_unaligned,
CE_DESC_RING_ALIGN);
- ce_ring->base_addr_ce_space = ALIGN(
- ce_ring->base_addr_ce_space_unaligned,
- CE_DESC_RING_ALIGN);
+ off = (unsigned long)ce_ring->base_addr_ce_space -
+ (unsigned long)ce_ring->base_addr_ce_space_unaligned;
+ ce_ring->base_addr_owner_space = (void *)
+ ((unsigned long)ce_ring->base_addr_owner_space_unaligned + off);
return ce_ring;
}
@@ -1007,8 +1019,8 @@
dma_free_coherent(ab->dev,
pipe->src_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
- pipe->src_ring->base_addr_owner_space,
- pipe->src_ring->base_addr_ce_space);
+ pipe->src_ring->base_addr_owner_space_unaligned,
+ pipe->src_ring->base_addr_ce_space_unaligned);
ATH11K_MEMORY_STATS_DEC(ab, ce_ring_alloc,
pipe->src_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN);
@@ -1021,8 +1033,8 @@
dma_free_coherent(ab->dev,
pipe->dest_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
- pipe->dest_ring->base_addr_owner_space,
- pipe->dest_ring->base_addr_ce_space);
+ pipe->dest_ring->base_addr_owner_space_unaligned,
+ pipe->dest_ring->base_addr_ce_space_unaligned);
ATH11K_MEMORY_STATS_DEC(ab, ce_ring_alloc,
pipe->dest_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN);
@@ -1036,8 +1048,8 @@
dma_free_coherent(ab->dev,
pipe->status_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
- pipe->status_ring->base_addr_owner_space,
- pipe->status_ring->base_addr_ce_space);
+ pipe->status_ring->base_addr_owner_space_unaligned,
+ pipe->status_ring->base_addr_ce_space_unaligned);
ATH11K_MEMORY_STATS_DEC(ab, ce_ring_alloc,
pipe->status_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN);
diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h
index c586847..a82285d 100644
--- a/drivers/net/wireless/ath/ath11k/ce.h
+++ b/drivers/net/wireless/ath/ath11k/ce.h
@@ -141,7 +141,7 @@
/* Host address space */
void *base_addr_owner_space_unaligned;
/* CE address space */
- u32 base_addr_ce_space_unaligned;
+ dma_addr_t base_addr_ce_space_unaligned;
/* Actual start of descriptors.
* Aligned to descriptor-size boundary.
@@ -151,7 +151,7 @@
void *base_addr_owner_space;
/* CE address space */
- u32 base_addr_ce_space;
+ dma_addr_t base_addr_ce_space;
/* HAL ring id */
u32 hal_ring_id;
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 00a5994..b5c4c1d 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
@@ -325,7 +326,7 @@
.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
.hw_ops = &ipq5018_ops,
.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074,
- .ring_mask = &ath11k_hw_ring_mask_ipq5018,
+ .ring_mask = &ath11k_hw_ring_mask_qcn6122,
.regs = &ipq5018_regs,
.m3_addr = ATH11K_QMI_IPQ5018_M3_DUMP_ADDRESS,
.spectral_fft_sz = 2,
@@ -363,7 +364,7 @@
.is_qdss_support = false,
.max_tx_ring = 1,
.wakeup_mhi = false,
- .reo_status_poll = true,
+ .reo_status_poll = false,
.cfr_support = true,
.cfr_dma_hdr_size = sizeof(struct ath11k_cfir_dma_hdr),
.cfr_num_stream_bufs = 255,
@@ -389,7 +390,7 @@
.hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074),
.hw_ops = &qcn6122_ops,
.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122,
- .ring_mask = &ath11k_hw_ring_mask_ipq5018,
+ .ring_mask = &ath11k_hw_ring_mask_qcn6122,
.regs = &qcn6122_regs,
.m3_addr = ATH11K_QMI_QCN6122_M3_DUMP_ADDRESS,
.spectral_fft_sz = 2,
@@ -427,7 +428,7 @@
.is_qdss_support = true,
.max_tx_ring = 1,
.wakeup_mhi = false,
- .reo_status_poll = true,
+ .reo_status_poll = false,
.cfr_support = true,
.cfr_dma_hdr_size = sizeof(struct ath11k_cfir_dma_hdr),
.cfr_num_stream_bufs = 255,
@@ -899,7 +900,8 @@
ab->bd_api = 2;
if (country_code && strlen(country_code) == 2) {
- strcpy(cc, country_code);
+ memcpy(cc, country_code, 2);
+ cc[2] = 0;
ath11k_country_str_tolower(cc);
scnprintf(filename, sizeof(filename),
"board-2-%s.bin", cc);
@@ -1052,7 +1054,7 @@
err_nss_tear:
ath11k_nss_teardown(ab);
err_dp_pdev_free:
- ath11k_dp_pdev_free(ab);
+ ath11k_dp_pdev_free(ab, true);
err_pdev_debug:
ath11k_debugfs_pdev_destroy(ab);
@@ -1070,7 +1072,7 @@
ath11k_nss_set_enabled(ab, false);
ath11k_hif_irq_disable(ab);
- ath11k_dp_pdev_free(ab);
+ ath11k_dp_pdev_free(ab, true);
ath11k_debugfs_pdev_destroy(ab);
}
@@ -1369,7 +1371,7 @@
ath11k_spectral_deinit(ab);
ath11k_thermal_unregister(ab);
ath11k_hif_irq_disable(ab);
- ath11k_dp_pdev_free(ab);
+ ath11k_dp_pdev_free(ab, false);
ath11k_hif_stop(ab);
ath11k_wmi_detach(ab);
ath11k_dp_pdev_reo_cleanup(ab);
@@ -1449,6 +1451,7 @@
ath11k_mac_drain_tx(ar);
complete(&ar->scan.started);
complete(&ar->scan.completed);
+ complete(&ar->scan.on_channel);
complete(&ar->peer_assoc_done);
complete(&ar->peer_delete_done);
complete(&ar->install_key_done);
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 09293d0..e135162 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -127,7 +127,7 @@
bool is_last_msdu;
bool is_continuation;
bool is_mcbc;
- bool is_eapol;
+ bool is_eapol_tkip;
struct hal_rx_desc *rx_desc;
u8 err_rel_src;
u8 err_code;
@@ -363,6 +363,8 @@
u32 tid_conf_changed[ATH11K_TID_MAX];
struct ath11k_tid_qos_config tid_cfg[ATH11K_TID_MAX];
u32 tids_rst;
+ u64 tbtt_offset;
+ struct work_struct update_bcn_template_work;
DECLARE_BITMAP(free_groupidx_map, ATH11K_GROUP_KEYS_NUM_MAX);
};
@@ -529,6 +531,7 @@
u64 tx_duration;
u32 tx_rts_retry_count;
u8 rssi_comb;
+ u32 tx_retry_count;
struct ewma_avg_rssi avg_rssi;
struct ath11k_htt_tx_stats *tx_stats;
struct ath11k_rx_peer_stats *rx_stats;
@@ -559,6 +562,7 @@
struct ewma_sta_per per;
u64 fail_pkts;
u64 succ_pkts;
+ u64 drop_pkts;
u64 msdu_cnt[ATH11K_TID_MAX];
u64 mpdu_cnt[ATH11K_TID_MAX];
u64 ppdu_cnt[ATH11K_TID_MAX];
@@ -568,9 +572,11 @@
struct ewma_sta_ber ber;
u64 succ_bytes;
u64 fail_bytes;
+ u64 drop_bytes;
/*bytes count for bit error rate computation*/
u32 ber_succ_bytes;
u32 ber_fail_bytes;
+ u32 last_tx_pkt_bw;
u8 sta_kickout;
unsigned long sta_kickout_timeout;
@@ -582,6 +588,9 @@
struct ath11k_per_peer_cfr_capture cfr_capture;
#endif
struct ath11k_smart_ant_sta *smart_ant_sta;
+ struct completion disassoc_comp;
+ bool tx_disassoc;
+ u32 bw_last;
};
#define ATH11K_HALF_20MHZ_BW 10
@@ -802,10 +811,17 @@
u32 afc_wfa_version;
bool is_6g_afc_expiry_event_received;
bool is_6g_afc_power_event_received;
+ bool switch_to_lpi_indication_received;
struct ath11k_afc_sp_reg_info *afc_reg_info;
bool afc_regdom_configured;
};
+enum mon_status_buf_done {
+ MON_STATUS_BUF_DONE = 0,
+ MON_STATUS_NO_BUF_DONE = 1,
+ MON_STATUS_BUF_DONE_NEXT = 2,
+};
+
struct ath11k {
struct ath11k_base *ab;
struct ath11k_pdev *pdev;
@@ -845,9 +861,11 @@
u32 max_tx_power;
u32 txpower_limit_2g;
u32 txpower_limit_5g;
+ u32 txpower_limit_6g;
u32 txpower_scale;
u32 power_scale;
u32 chan_tx_pwr;
+ s32 chan_noise_floor;
u32 num_stations;
u32 max_num_stations;
bool monitor_conf_enabled;
@@ -972,11 +990,16 @@
u32 chan_bw_interference_bitmap;
bool awgn_intf_handling_in_prog;
struct ath11k_rx_buf_id rx_buf_id;
+ u8 mgmt_retry_limit;
/* fw pdev_stats can be requested by get_txpower mac ops too */
struct list_head fw_stats_pdevs;
struct completion fw_stats_complete;
bool fw_stats_done;
+ u16 rts_threshold[NUM_NL80211_IFTYPES];
+ struct completion ani_status_event;
+ bool mon_status_skb_zero;
+ enum mon_status_buf_done mon_status_no_buf_done;
};
struct ath11k_band_cap {
@@ -1065,7 +1088,9 @@
u32 err_ring_pkts;
u32 invalid_rbm;
u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX];
+ u32 rxdma_error_drop[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX];
u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX];
+ u32 reo_error_drop[HAL_REO_DEST_RING_ERROR_CODE_MAX];
u32 hal_reo_error[DP_REO_DST_RING_MAX];
struct ath11k_soc_dp_tx_err_stats tx_err;
struct ath11k_dp_ring_bp_stats bp_stats;
@@ -1159,6 +1184,18 @@
struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS];
struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS];
unsigned long long free_vdev_map;
+
+ /*
+ * The rhashtable containing struct ath11k_peer keyed by mac addr
+ * protected under ab->base_lock spin lock
+ */
+ struct rhashtable *rhead_peer_addr;
+ struct rhashtable_params rhash_peer_addr_param;
+
+ /* The rhashtable containing struct ath11k_peer keyed by id */
+ struct rhashtable *rhead_peer_id;
+ struct rhashtable_params rhash_peer_id_param;
+
struct list_head peers;
wait_queue_head_t peer_mapping_wq;
u8 mac_addr[ETH_ALEN];
@@ -1256,6 +1293,8 @@
struct mutex base_ast_lock;
struct work_struct wmi_ast_work;
struct list_head wmi_ast_list;
+ u32 napi_poll_budget;
+ bool gro_support_enabled;
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
diff --git a/drivers/net/wireless/ath/ath11k/debug_nss.c b/drivers/net/wireless/ath/ath11k/debug_nss.c
index 74f2131..2899859 100644
--- a/drivers/net/wireless/ath/ath11k/debug_nss.c
+++ b/drivers/net/wireless/ath/ath11k/debug_nss.c
@@ -11,6 +11,8 @@
#include "debug.h"
#include "debug_nss.h"
+extern struct dentry *debugfs_debug_infra;
+
static unsigned int
debug_nss_fill_mpp_dump(struct ath11k_vif *arvif, char *buf, ssize_t size)
{
@@ -1007,16 +1009,17 @@
void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab)
{
- struct dentry *debugfs_dbg_infra;
+ if (debugfs_debug_infra)
+ return;
- debugfs_dbg_infra = debugfs_create_dir("dbg_infra", debugfs_ath11k);
+ debugfs_debug_infra = debugfs_create_dir("dbg_infra", debugfs_ath11k);
debugfs_create_file("links", 0200,
- debugfs_dbg_infra, ab,
+ debugfs_debug_infra, ab,
&fops_nss_links);
debugfs_create_file("mpp_mode", 0600,
- debugfs_dbg_infra, ab,
+ debugfs_debug_infra, ab,
&fops_nss_mpp_mode);
}
diff --git a/drivers/net/wireless/ath/ath11k/debug_smart_ant.c b/drivers/net/wireless/ath/ath11k/debug_smart_ant.c
index 808f836..8fa98f2 100644
--- a/drivers/net/wireless/ath/ath11k/debug_smart_ant.c
+++ b/drivers/net/wireless/ath/ath11k/debug_smart_ant.c
@@ -56,6 +56,12 @@
return count;
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
if (enable) {
ret = ath11k_wmi_pdev_enable_smart_ant(ar,
ATH11K_SMART_ANT_ENABLE,
@@ -113,6 +119,14 @@
if (len < ATH11K_SA_TX_ANT_MIN_LEN)
return -EINVAL;
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+ mutex_unlock(&ar->conf_mutex);
+
buf[len] = '\0';
sptr = buf;
for (i = 0; i < ETH_ALEN - 1; i++) {
@@ -198,6 +212,11 @@
"Setting Rx antenna to %d\n", rxant);
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
ret = ath11k_wmi_pdev_set_rx_ant(ar, rxant);
mutex_unlock(&ar->conf_mutex);
@@ -245,6 +264,14 @@
char *token, *sptr;
char buf[128];
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+ mutex_unlock(&ar->conf_mutex);
+
if (!ath11k_smart_ant_enabled(ar))
return -ENOTSUPP;
diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c
index 2562397..eb15353 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs.c
@@ -17,6 +17,7 @@
#include "qmi.h"
struct dentry *debugfs_ath11k;
+struct dentry *debugfs_debug_infra;
static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
"REO2SW1_RING",
@@ -258,9 +259,18 @@
{
struct ath11k_vif *arvif = file->private_data;
struct wmi_ctrl_path_stats_cmd_param param = {0};
+ struct ath11k *ar = arvif->ar;
u8 buf[128] = {0};
int ret;
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+ mutex_unlock(&ar->conf_mutex);
+
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
if (ret < 0) {
return ret;
@@ -709,10 +719,19 @@
{
struct ath11k_vif *arvif = file->private_data;
struct ath11k_base *ab = arvif->ar->ab;
+ struct ath11k *ar = arvif->ar;
unsigned int tx_aggr_size = 0;
int ret;
struct set_custom_aggr_size_params params = {0};
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+ mutex_unlock(&ar->conf_mutex);
+
if (kstrtouint_from_user(ubuf, count, 0, &tx_aggr_size))
return -EINVAL;
@@ -1543,13 +1562,15 @@
soc_stats->invalid_rbm);
len += scnprintf(buf + len, size - len, "RXDMA errors:\n");
for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)
- len += scnprintf(buf + len, size - len, "%s: %u\n",
- rxdma_err[i], soc_stats->rxdma_error[i]);
+ len += scnprintf(buf + len, size - len, "%s: handled %u dropped %u\n",
+ rxdma_err[i], soc_stats->rxdma_error[i],
+ soc_stats->rxdma_error_drop[i]);
len += scnprintf(buf + len, size - len, "\nREO errors:\n");
for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)
- len += scnprintf(buf + len, size - len, "%s: %u\n",
- reo_err[i], soc_stats->reo_error[i]);
+ len += scnprintf(buf + len, size - len, "%s: handled %u dropped %u\n",
+ reo_err[i], soc_stats->reo_error[i],
+ soc_stats->reo_error_drop[i]);
len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");
len += scnprintf(buf + len, size - len,
@@ -2176,7 +2197,7 @@
goto exit;
}
- ret = ath11k_wmi_pdev_set_param(ar, ATH11K_AGGR_SW_RETRY_THRESHOLD,
+ ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
retry_thold, pdev->pdev_id);
if (ret) {
@@ -2268,6 +2289,12 @@
debugfs_create_file("set_fw_recovery", 0600, ab->debugfs_soc, ab,
&fops_fw_recovery);
+ debugfs_create_file("enable_memory_stats", 0600, ab->debugfs_soc,
+ ab, &fops_enable_memory_stats);
+
+ debugfs_create_file("memory_stats", 0600, ab->debugfs_soc, ab,
+ &fops_memory_stats);
+
debugfs_create_file("ce_latency_stats", 0600, ab->debugfs_soc, ab,
&fops_ce_latency_stats);
debugfs_create_file("fw_dbglog_config", 0600, ab->debugfs_soc, ab,
@@ -2342,6 +2369,7 @@
{
debugfs_remove_recursive(debugfs_ath11k);
debugfs_ath11k = NULL;
+ debugfs_debug_infra = NULL;
}
void ath11k_debugfs_fw_stats_init(struct ath11k *ar)
@@ -2654,12 +2682,6 @@
}
}
- debugfs_create_file("enable_memory_stats", 0600, ab->debugfs_soc,
- ab, &fops_enable_memory_stats);
-
- debugfs_create_file("memory_stats", 0600, ab->debugfs_soc, ab,
- &fops_memory_stats);
-
#define HTT_RX_FILTER_TLV_LITE_MODE \
(HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \
HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \
@@ -2765,6 +2787,12 @@
struct ath11k *ar = file->private_data;
int ret;
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
ret = ath11k_wmi_simulate_radar(ar);
if (ret)
return ret;
@@ -2848,10 +2876,6 @@
mutex_lock(&ar->conf_mutex);
arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);
- if (!arvif->is_started) {
- ret = -EINVAL;
- goto exit;
- }
if(coex == 1 && !test_bit(ATH11K_FLAG_BTCOEX, &ar->dev_flags))
set_bit(ATH11K_FLAG_BTCOEX, &ar->dev_flags);
@@ -2941,11 +2965,6 @@
mutex_lock(&ar->conf_mutex);
arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);
- if (!arvif->is_started) {
- ret = -EINVAL;
- goto exit;
- }
-
coex_config.vdev_id = arvif->vdev_id;
coex_config.config_type = WMI_COEX_CONFIG_AP_TDM;
coex_config.duty_cycle = duty_cycle;
@@ -3009,11 +3028,6 @@
mutex_lock(&ar->conf_mutex);
arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);
- if (!arvif->is_started) {
- ret = -EINVAL;
- goto exit;
- }
-
ar->coex.coex_algo_type = coex_algo;
coex_config.vdev_id = arvif->vdev_id;
coex_config.config_type = WMI_COEX_CONFIG_FORCED_ALGO;
@@ -3198,6 +3212,12 @@
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
if (ar->ps_state_enable == ps_state_enable) {
ret = count;
goto exit;
@@ -3464,6 +3484,14 @@
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+
buf = vmalloc(count);
if (!buf) {
ret = -ENOMEM;
@@ -4345,11 +4373,35 @@
size_t count, loff_t *ppos)
{
struct ath11k *ar = file->private_data;
- int len = 0;
- char buf[32];
+ unsigned long time_left;
+ int ret;
+ int len;
+ char buf[128];
- len = scnprintf(buf, sizeof(buf) - len, "%d\n",ar->ani_enabled);
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto unlock;
+ }
+ reinit_completion(&ar->ani_status_event);
+ ret = ath11k_wmi_pdev_get_ani_status(ar);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to get ani status: %d\n", ret);
+ goto unlock;
+ }
+ time_left = wait_for_completion_timeout(&ar->ani_status_event,
+ 1 * HZ);
+ if (time_left == 0) {
+ ret = -ETIMEDOUT;
+ goto unlock;
+ }
+ len = scnprintf(buf, sizeof(buf), "%d\n", ar->ani_enabled);
+ mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+unlock:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
}
static ssize_t ath11k_write_ani_enable(struct file *file,
@@ -4365,6 +4417,12 @@
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d is not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
if (ar->ani_enabled == enable) {
ret = count;
goto exit;
@@ -4376,7 +4434,9 @@
ath11k_warn(ar->ab, "ani_enable failed from debugfs: %d\n", ret);
goto exit;
}
+ spin_lock_bh(&ar->data_lock);
ar->ani_enabled = enable;
+ spin_unlock_bh(&ar->data_lock);
ret = count;
exit:
@@ -4420,6 +4480,12 @@
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ANI_POLL_PERIOD,
ani_poll_period, ar->pdev->pdev_id);
if (ret) {
@@ -4470,6 +4536,12 @@
mutex_lock(&ar->conf_mutex);
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
ani_listen_period, ar->pdev->pdev_id);
if (ret) {
@@ -4893,22 +4965,26 @@
size_t count, loff_t *ppos)
{
struct ath11k *ar = file->private_data;
- int rts_threshold, ret = 0;
+ int rts_threshold, ret = 0, vif_type;
struct ath11k_vif *arvif;
struct ieee80211_vif *vif;
+ char buf[128] = {0};
- ret = kstrtoint_from_user(user_buf, count, 0, &rts_threshold);
- if (ret)
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+ if (ret < 0)
+ return ret;
+
+ ret = sscanf(buf, "%d %d", &rts_threshold, &vif_type);
+ if (ret || vif_type >= NUM_NL80211_IFTYPES)
return ret;
mutex_lock(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
vif = arvif->vif;
- if (vif->type != NL80211_IFTYPE_MESH_POINT) {
- ar->hw->wiphy->rts_threshold = rts_threshold;
+ if (vif->type == vif_type) {
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_RTS_THRESHOLD,
- ar->hw->wiphy->rts_threshold);
+ rts_threshold);
if (ret) {
ath11k_warn(ar->ab,
"Failed to set rts threshold for vdev %d: %d\n",
@@ -4918,6 +4994,7 @@
}
}
+ ar->rts_threshold[vif_type] = rts_threshold;
mutex_unlock(&ar->conf_mutex);
return count;
}
@@ -4927,12 +5004,15 @@
size_t count, loff_t *ppos)
{
struct ath11k *ar = file->private_data;
- char buf[32] = {0};
- int len = 0;
+ char buf[2048] = {0};
+ int len = 0, i;
mutex_lock(&ar->conf_mutex);
- len = scnprintf(buf, sizeof(buf) - len, "%d\n",
- ar->hw->wiphy->rts_threshold);
+ len += scnprintf(buf, sizeof(buf) - len, "VIF type\tRTS threshold\n");
+ for (i = 0; i < NUM_NL80211_IFTYPES; i++) {
+ if (ar->rts_threshold[i])
+ len += scnprintf(buf + len, sizeof(buf) - len, "%d\t\t%d\n", i, ar->rts_threshold[i]);
+ }
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -5064,6 +5144,159 @@
.llseek = default_llseek,
};
+static ssize_t ath11k_read_napi_poll_budget(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ int len = 0;
+ char buf[32];
+
+ len = scnprintf(buf, sizeof(buf) - len, "%u\n", ar->ab->napi_poll_budget);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath11k_write_napi_poll_budget(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ int ret;
+ u32 napi_poll_budget = 0;
+
+ if (kstrtou32_from_user(user_buf, count, 0, &napi_poll_budget))
+ return -EINVAL;
+
+ if(napi_poll_budget > NAPI_POLL_WEIGHT)
+ return -EINVAL;
+
+ if(napi_poll_budget < 4)
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+ ar->ab->napi_poll_budget = napi_poll_budget;
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static const struct file_operations fops_napi_poll_budget = {
+ .read = ath11k_read_napi_poll_budget,
+ .write = ath11k_write_napi_poll_budget,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath11k_read_gro_support(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ int len = 0;
+ char buf[32];
+
+ len = scnprintf(buf, sizeof(buf) - len, "%u\n",
+ ar->ab->gro_support_enabled);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath11k_write_gro_support(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ int ret;
+ bool gro_support = false;
+
+ if (kstrtobool_from_user(user_buf, count, &gro_support))
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+ ar->ab->gro_support_enabled = gro_support;
+ ret = count;
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static const struct file_operations fops_gro_support = {
+ .read = ath11k_read_gro_support,
+ .write = ath11k_write_gro_support,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath11k_write_mgmt_retry_limit(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ struct ath11k_pdev *pdev = ar->pdev;
+ u8 retry_limit;
+ int ret;
+
+ if (kstrtou8_from_user(user_buf, count, 0, &retry_limit))
+ return -EINVAL;
+
+ if (retry_limit < ATH11K_MIN_MGMT_RETRY_LIMIT_COUNT ||
+ retry_limit > ATH11K_MAX_MGMT_RETRY_LIMIT_COUNT)
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->mgmt_retry_limit == retry_limit) {
+ ret = count;
+ goto exit;
+ }
+
+ if (ar->state != ATH11K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_MGMT_RETRY_LIMIT,
+ retry_limit, pdev->pdev_id);
+
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set mgmt retry limit: %d\n", ret);
+ goto exit;
+ }
+
+ ar->mgmt_retry_limit = retry_limit;
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static ssize_t ath11k_read_mgmt_retry_limit(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ int len = 0;
+ char buf[8];
+
+ mutex_lock(&ar->conf_mutex);
+ len = scnprintf(buf, sizeof(buf) - len, "%d\n",
+ ar->mgmt_retry_limit);
+ mutex_unlock(&ar->conf_mutex);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_mgmt_retry_limit = {
+ .read = ath11k_read_mgmt_retry_limit,
+ .write = ath11k_write_mgmt_retry_limit,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath11k_debugfs_register(struct ath11k *ar)
{
struct ath11k_base *ab = ar->ab;
@@ -5106,8 +5339,9 @@
ath11k_init_pktlog(ar);
ath11k_smart_ant_debugfs_init(ar);
init_completion(&ar->tpc_complete);
- init_completion(&ab->ani_ofdm_event);
- init_completion(&ab->ani_cck_event);
+ init_completion(&ab->ani_ofdm_event);
+ init_completion(&ab->ani_cck_event);
+ init_completion(&ar->ani_status_event);
debugfs_create_file("ext_tx_stats", 0644,
ar->debug.debugfs_pdev, ar,
@@ -5137,6 +5371,12 @@
debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_pdev, ar,
&fops_ps_state_enable);
+ debugfs_create_file("napi_poll_budget", 0644, ar->debug.debugfs_pdev, ar,
+ &fops_napi_poll_budget);
+
+ debugfs_create_file("gro_support_enabled", 0644, ar->debug.debugfs_pdev,
+ ar, &fops_gro_support);
+
if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT,
ar->ab->wmi_ab.svc_map)) {
debugfs_create_file("ps_timekeeper_enable", 0600,
@@ -5207,6 +5447,9 @@
debugfs_create_file("ftm_resp", 0600,
ar->debug.debugfs_pdev,
ar, &fops_ftm_resp);
+ debugfs_create_file("mgmt_retry_limit", 0600,
+ ar->debug.debugfs_pdev,
+ ar, &fops_mgmt_retry_limit);
if (!ab->userpd_id) {
debugfs_create_file("tx_wmi_mgmt", 0600,
diff --git a/drivers/net/wireless/ath/ath11k/debugfs.h b/drivers/net/wireless/ath/ath11k/debugfs.h
index bb6ad4c..59d9f5b 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs.h
+++ b/drivers/net/wireless/ath/ath11k/debugfs.h
@@ -240,6 +240,9 @@
#define ATH11K_MAX_AGGR_RETRY_COUNT 255
#define ATH11K_AGGR_SW_RETRY_THRESHOLD 50
+#define ATH11K_MIN_MGMT_RETRY_LIMIT_COUNT 1
+#define ATH11K_MAX_MGMT_RETRY_LIMIT_COUNT 16
+
#ifdef CONFIG_ATH11K_DEBUGFS
int ath11k_debugfs_create(void);
void ath11k_debugfs_destroy(void);
diff --git a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c
index 19c03e0..c2dea31 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c
@@ -6338,6 +6338,13 @@
return -E2BIG;
mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH11K_STATE_ON) {
+ ath11k_warn(ar->ab, "pdev %d not in ON state\n", ar->pdev->pdev_id);
+ mutex_unlock(&ar->conf_mutex);
+ return -ENETDOWN;
+ }
+
cfg_params.cfg0 = HTT_STAT_DEFAULT_RESET_START_OFFSET;
cfg_params.cfg1 = 1 << (cfg_params.cfg0 + type);
ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar,
diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c
index 71201e7..2daa806 100644
--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c
@@ -324,9 +324,6 @@
[HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY] = "MEC notify pkt count"};
int idx;
- if (!arsta->tx_stats || !arsta->wbm_tx_stats)
- return -ENOENT;
-
buf = kzalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -334,6 +331,12 @@
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
+
+ if (!arsta->tx_stats || !arsta->wbm_tx_stats) {
+ retval = -ENOENT;
+ goto end;
+ }
+
for (k = 0; k < ATH11K_STATS_TYPE_MAX; k++) {
for (j = 0; j < ATH11K_COUNTER_TYPE_MAX; j++) {
stats = &arsta->tx_stats->stats[k];
@@ -477,6 +480,12 @@
mutex_unlock(&ar->conf_mutex);
return retval;
+
+end:
+ spin_unlock_bh(&ar->data_lock);
+ mutex_unlock(&ar->conf_mutex);
+ kfree(buf);
+ return retval;
}
static const struct file_operations fops_tx_stats = {
@@ -1287,15 +1296,18 @@
const int size = ATH11K_DRV_TX_STATS_SIZE;
char *buf;
- if (!arsta->tx_stats)
- return -ENOENT;
-
buf = kzalloc(ATH11K_DRV_TX_STATS_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->ab->base_lock);
+
+ if (!arsta->tx_stats) {
+ ret_val = -ENOENT;
+ goto end;
+ }
+
len += scnprintf(buf + len, size - len,
"Tx packets inflow from mac80211: %u\n",
atomic_read(&arsta->drv_tx_pkts.pkts_in));
@@ -1312,6 +1324,11 @@
mutex_unlock(&ar->conf_mutex);
return ret_val;
+end:
+ spin_unlock_bh(&ar->ab->base_lock);
+ mutex_unlock(&ar->conf_mutex);
+ kfree(buf);
+ return ret_val;
}
static const struct file_operations fops_driver_tx_pkts_flow = {
@@ -1330,9 +1347,6 @@
struct ath11k *ar = arsta->arvif->ar;
int ret, reset;
- if (!arsta->tx_stats || !arsta->wbm_tx_stats)
- return -ENOENT;
-
ret = kstrtoint_from_user(buf, count, 0, &reset);
if (ret)
return ret;
@@ -1341,6 +1355,12 @@
return -EINVAL;
spin_lock_bh(&ar->ab->base_lock);
+
+ if (!arsta->tx_stats || !arsta->wbm_tx_stats) {
+ spin_unlock_bh(&ar->ab->base_lock);
+ return -ENOENT;
+ }
+
memset(arsta->tx_stats, 0, sizeof(*arsta->tx_stats));
atomic_set(&arsta->drv_tx_pkts.pkts_in, 0);
atomic_set(&arsta->drv_tx_pkts.pkts_out, 0);
@@ -1586,12 +1606,16 @@
arsta->fail_pkts);
len += scnprintf(buf + len, size - len, "succ_pkts : %llu\n",
arsta->succ_pkts);
+ len += scnprintf(buf + len, size - len, "drop_pkts : %llu\n",
+ arsta->drop_pkts);
len += scnprintf(buf + len, size - len, "PER : %lu\n",
ewma_sta_per_read(&arsta->per));
len += scnprintf(buf + len, size - len, "fail_bytes : %llu\n",
arsta->fail_bytes);
len += scnprintf(buf + len, size - len, "succ_bytes : %llu\n",
arsta->succ_bytes);
+ len += scnprintf(buf + len, size - len, "drop_bytes : %llu\n",
+ arsta->drop_bytes);
len += scnprintf(buf + len, size - len,
"BER : %lu\n", ewma_sta_ber_read(&arsta->ber));
@@ -1668,17 +1692,24 @@
int len = 0, i, retval = 0;
u64 total_succ_bytes;
- if (!arsta->tx_stats || !arsta->wbm_tx_stats) {
- ath11k_warn(ar->ab, "failed to get tx success bytes");
- return -EINVAL;
- }
-
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
+ if (!arsta->tx_stats || !arsta->wbm_tx_stats) {
+ ath11k_warn(ar->ab, "failed to get tx success bytes");
+ retval = -EINVAL;
+ goto end;
+ }
+
stats = &arsta->tx_stats->stats[ATH11K_STATS_TYPE_SUCC];
+ if (!stats) {
+ ath11k_warn(ar->ab, "Stats not present to read Tx success bytes\n");
+ retval = -EINVAL;
+ goto end;
+ }
+
total_succ_bytes = stats->gi[ATH11K_COUNTER_TYPE_BYTES][0] +
stats->gi[ATH11K_COUNTER_TYPE_BYTES][1] +
stats->gi[ATH11K_COUNTER_TYPE_BYTES][2] +
@@ -1697,6 +1728,11 @@
mutex_unlock(&ar->conf_mutex);
return retval;
+
+end:
+ spin_unlock_bh(&ar->data_lock);
+ mutex_unlock(&ar->conf_mutex);
+ return retval;
}
static const struct file_operations fops_tx_success_bytes = {
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index bcbc612..9879795 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -251,6 +251,8 @@
int max_entries = ath11k_hal_srng_get_max_entries(ab, type);
int ret;
bool cached;
+ unsigned long off;
+ u8 align = HAL_RING_BASE_ALIGN;
if (max_entries < 0 || entry_sz < 0)
return -EINVAL;
@@ -258,6 +260,9 @@
if (num_entries > max_entries)
num_entries = max_entries;
+ if (type == HAL_RXDMA_DIR_BUF)
+ align = HAL_RXDMA_DIR_BUF_RING_BASE_ALIGN;
+
/* Allocate the reo dst and tx completion rings from cacheable memory */
switch (type) {
case HAL_REO_DST:
@@ -273,7 +278,7 @@
if (ath11k_nss_offload_enabled(ab))
cached = false;
- ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1;
+ ring->size = (num_entries * entry_sz) + align - 1;
if (!cached) {
ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
@@ -289,9 +294,10 @@
ATH11K_MEMORY_STATS_INC(ab, dma_alloc, ring->size);
- ring->vaddr = PTR_ALIGN(ring->vaddr_unaligned, HAL_RING_BASE_ALIGN);
- ring->paddr = ring->paddr_unaligned + ((unsigned long)ring->vaddr -
- (unsigned long)ring->vaddr_unaligned);
+ ring->paddr = (dma_addr_t) ALIGN((unsigned long)ring->paddr_unaligned,
+ align);
+ off = (unsigned long)ring->paddr - (unsigned long)ring->paddr_unaligned;
+ ring->vaddr = (u32 *) ((unsigned long)ring->vaddr_unaligned + off);
params.ring_base_vaddr = ring->vaddr;
params.ring_base_paddr = ring->paddr;
@@ -390,27 +396,7 @@
struct ath11k_dp *dp = from_timer(dp, timer, reo_status_timer);
struct ath11k_base *ab = dp->ab;
- spin_lock_bh(&dp->reo_cmd_lock);
- dp->reo_status_timer_running = false;
- spin_unlock_bh(&dp->reo_cmd_lock);
-
ath11k_dp_process_reo_status(ab);
-}
-
-void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab)
-{
- struct ath11k_dp *dp = &ab->dp;
-
- if (!ab->hw_params.reo_status_poll)
- return;
-
- spin_lock_bh(&dp->reo_cmd_lock);
- if (dp->reo_status_timer_running) {
- spin_unlock_bh(&dp->reo_cmd_lock);
- return;
- }
- dp->reo_status_timer_running = true;
- spin_unlock_bh(&dp->reo_cmd_lock);
mod_timer(&dp->reo_status_timer, jiffies +
msecs_to_jiffies(ATH11K_REO_STATUS_POLL_TIMEOUT_MS));
@@ -424,7 +410,6 @@
return;
del_timer_sync(&dp->reo_status_timer);
- dp->reo_status_timer_running = false;
}
static void ath11k_dp_init_reo_status_timer(struct ath11k_base *ab)
@@ -436,6 +421,10 @@
timer_setup(&dp->reo_status_timer,
ath11k_dp_handle_reo_status_timer, 0);
+
+ /* Trigger reo status polling periodically */
+ mod_timer(&dp->reo_status_timer, jiffies +
+ msecs_to_jiffies(ATH11K_REO_STATUS_POLL_TIMEOUT_MS));
}
static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab)
@@ -898,6 +887,10 @@
int i = 0, j;
int tot_work_done = 0;
bool nss_offload;
+ int original_budget = budget;
+
+ if (budget != ab->napi_poll_budget)
+ budget = ab->napi_poll_budget;
/* Processing of offloaded rings are not required */
nss_offload = ath11k_nss_offload_enabled(ab);
@@ -988,11 +981,14 @@
/* TODO: Implement handler for other interrupts */
done:
+ // Don't call napi_complete_done() if there is more work to do
+ if (tot_work_done >= ab->napi_poll_budget)
+ tot_work_done = original_budget;
return tot_work_done;
}
EXPORT_SYMBOL(ath11k_dp_service_srng);
-void ath11k_dp_pdev_free(struct ath11k_base *ab)
+void ath11k_dp_pdev_free(struct ath11k_base *ab, bool ureg_dbgfs)
{
struct ath11k *ar;
int i;
@@ -1002,7 +998,8 @@
for (i = 0; i < ab->num_radios; i++) {
ar = ab->pdevs[i].ar;
ath11k_dp_rx_pdev_free(ab, i);
- ath11k_debugfs_unregister(ar);
+ if (ureg_dbgfs)
+ ath11k_debugfs_unregister(ar);
ath11k_dp_rx_pdev_mon_detach(ar);
}
}
@@ -1057,7 +1054,7 @@
return 0;
err:
- ath11k_dp_pdev_free(ab);
+ ath11k_dp_pdev_free(ab, true);
return ret;
}
@@ -1243,6 +1240,7 @@
for (i = 0; i < ab->hw_params.num_dscp_tid_map_tbl; i++)
ath11k_hal_tx_set_dscp_tid_map(ab, i);
+ ab->napi_poll_budget = NAPI_POLL_WEIGHT;
/* Init any SOC level resource for DP */
return 0;
diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
index 46a9ad6..5dc9061 100644
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -45,7 +45,7 @@
#define DP_MON_PURGE_TIMEOUT_MS 100
#define DP_MON_SERVICE_BUDGET 128
-#define ATH11K_REO_STATUS_POLL_TIMEOUT_MS 10
+#define ATH11K_REO_STATUS_POLL_TIMEOUT_MS 50
struct dp_reo_cache_flush_elem {
struct list_head list;
@@ -307,7 +307,6 @@
/* reo status timer and flags */
struct timer_list reo_status_timer;
- bool reo_status_timer_running;
};
/* HTT definitions */
@@ -1960,7 +1959,7 @@
int ath11k_dp_alloc(struct ath11k_base *ab);
int ath11k_dp_pdev_alloc(struct ath11k_base *ab);
void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab);
-void ath11k_dp_pdev_free(struct ath11k_base *ab);
+void ath11k_dp_pdev_free(struct ath11k_base *ab, bool ureg_dbgfs);
int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id,
int mac_id, enum hal_ring_type ring_type);
int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr);
@@ -1985,6 +1984,5 @@
struct ath11k_hp_update_timer *update_timer,
u32 interval, u32 ring_id);
void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab);
-void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 56f7ce6..6a8809c 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -434,12 +434,12 @@
buf_id = buf_ids[buf_id_index];
idr_replace(&rx_ring->bufs_idr, skb, buf_id);
} else {
- buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0,
- rx_ring->bufs_max * 3, GFP_ATOMIC);
+ buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 1,
+ (rx_ring->bufs_max * 3) + 1, GFP_ATOMIC);
}
spin_unlock_bh(&rx_ring->idr_lock);
- if (buf_id < 0)
+ if (buf_id <= 0)
goto fail_free_skb;
desc = ath11k_hal_srng_src_get_next_entry(ab, srng);
@@ -767,7 +767,7 @@
}
}
-static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab,
+static int ath11k_dp_reo_cache_flush(struct ath11k_base *ab,
struct dp_rx_tid *rx_tid)
{
struct ath11k_hal_reo_cmd cmd = {0};
@@ -786,8 +786,15 @@
NULL);
if (ret)
ath11k_warn(ab,
- "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d)\n",
- rx_tid->tid, ret);
+ "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d) desc_sz(%ld)\n",
+ rx_tid->tid, ret, desc_sz);
+
+ /* If this fails with ring full condition, then
+ * no need to retry below as it is expected to
+ * fail within short time
+ */
+ if (ret == -ENOBUFS)
+ goto exit;
}
memset(&cmd, 0, sizeof(cmd));
@@ -797,14 +804,12 @@
ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid,
HAL_REO_CMD_FLUSH_CACHE,
&cmd, ath11k_dp_reo_cmd_free);
- if (ret) {
+ if (ret)
ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n",
rx_tid->tid, ret);
- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size,
- DMA_BIDIRECTIONAL);
- kfree(rx_tid->vaddr);
- rx_tid->vaddr = NULL;
- }
+
+exit:
+ return ret;
}
static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
@@ -843,13 +848,22 @@
if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD ||
time_after(jiffies, elem->ts +
msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) {
+ spin_unlock_bh(&dp->reo_cmd_lock);
+ if (ath11k_dp_reo_cache_flush(ab, &elem->data)) {
+ /* In failure case, just update the timestamp
+ * for flush cache elem and continue
+ */
+ spin_lock_bh(&dp->reo_cmd_lock);
+ elem->ts = jiffies +
+ msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS);
+ ath11k_err(ab, "Failed to send HAL_REO_CMD_FLUSH_CACHE cmd"
+ "Updating timestamp (%ld) in the list\n", elem->ts);
+ continue;
+ }
+ spin_lock_bh(&dp->reo_cmd_lock);
list_del(&elem->list);
dp->reo_cmd_cache_flush_count--;
- spin_unlock_bh(&dp->reo_cmd_lock);
-
- ath11k_dp_reo_cache_flush(ab, &elem->data);
kfree(elem);
- spin_lock_bh(&dp->reo_cmd_lock);
}
}
spin_unlock_bh(&dp->reo_cmd_lock);
@@ -1573,95 +1587,96 @@
ru_start = user_rate->ru_start;
ru_tone = user_rate->ru_end;
- /* Note: If host configured fixed rates and in some other special
- * cases, the broadcast/management frames are sent in different rates.
- * Firmware rate's control to be skipped for this?
- */
-
- if (flags == WMI_RATE_PREAMBLE_HE && mcs > 11) {
- ath11k_warn(ab, "Invalid HE mcs %d peer stats", mcs);
- return;
- }
-
- if (flags == WMI_RATE_PREAMBLE_HE && mcs > ATH11K_HE_MCS_MAX) {
- ath11k_warn(ab, "Invalid HE mcs %d peer stats", mcs);
- return;
- }
-
- if (flags == WMI_RATE_PREAMBLE_VHT && mcs > ATH11K_VHT_MCS_MAX) {
- ath11k_warn(ab, "Invalid VHT mcs %d peer stats", mcs);
- return;
- }
-
- if (flags == WMI_RATE_PREAMBLE_HT && (mcs > ATH11K_HT_MCS_MAX || nss < 1)) {
- ath11k_warn(ab, "Invalid HT mcs %d nss %d peer stats",
- mcs, nss);
- return;
- }
-
- if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) {
- ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs,
- flags,
- &rate_idx,
- &rate);
- if (ret < 0)
- return;
- }
-
- rcu_read_lock();
- spin_lock_bh(&ab->base_lock);
- peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id);
-
- if (!peer || !peer->sta) {
- spin_unlock_bh(&ab->base_lock);
- rcu_read_unlock();
- return;
- }
-
- sta = peer->sta;
- arsta = (struct ath11k_sta *)sta->drv_priv;
-
- memset(&arsta->txrate, 0, sizeof(arsta->txrate));
-
- switch (flags) {
- case WMI_RATE_PREAMBLE_OFDM:
- arsta->txrate.legacy = rate;
- break;
- case WMI_RATE_PREAMBLE_CCK:
- arsta->txrate.legacy = rate;
- break;
- case WMI_RATE_PREAMBLE_HT:
- arsta->txrate.mcs = mcs + 8 * (nss - 1);
- arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
- if (sgi)
- arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
- break;
- case WMI_RATE_PREAMBLE_VHT:
- arsta->txrate.mcs = mcs;
- arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
- if (sgi)
- arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
- break;
- case WMI_RATE_PREAMBLE_HE:
- arsta->txrate.mcs = mcs;
- arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
- arsta->txrate.he_dcm = dcm;
- arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi);
- arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(
- (user_rate->ru_end -
- user_rate->ru_start) + 1);
- break;
- }
-
- arsta->txrate.nss = nss;
- arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);
- arsta->tx_duration += tx_duration;
- memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info));
-
/* PPDU stats reported for mgmt packet doesn't have valid tx bytes.
* So skip peer stats update for mgmt packets.
*/
+
if (tid < HTT_PPDU_STATS_NON_QOS_TID) {
+ /* Note: If host configured fixed rates and in some other special
+ * cases, the broadcast/management frames are sent in different rates.
+ * Firmware rate's control to be skipped for this?
+ */
+
+ if (flags == WMI_RATE_PREAMBLE_HE && mcs > 11) {
+ ath11k_warn(ab, "Invalid HE mcs %d peer stats", mcs);
+ return;
+ }
+
+ if (flags == WMI_RATE_PREAMBLE_HE && mcs > ATH11K_HE_MCS_MAX) {
+ ath11k_warn(ab, "Invalid HE mcs %d peer stats", mcs);
+ return;
+ }
+
+ if (flags == WMI_RATE_PREAMBLE_VHT && mcs > ATH11K_VHT_MCS_MAX) {
+ ath11k_warn(ab, "Invalid VHT mcs %d peer stats", mcs);
+ return;
+ }
+
+ if (flags == WMI_RATE_PREAMBLE_HT && (mcs > ATH11K_HT_MCS_MAX || nss < 1)) {
+ ath11k_warn(ab, "Invalid HT mcs %d nss %d peer stats",
+ mcs, nss);
+ return;
+ }
+
+ if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) {
+ ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs,
+ flags,
+ &rate_idx,
+ &rate);
+ if (ret < 0)
+ return;
+ }
+
+ rcu_read_lock();
+ spin_lock_bh(&ab->base_lock);
+ peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id);
+
+ if (!peer || !peer->sta) {
+ spin_unlock_bh(&ab->base_lock);
+ rcu_read_unlock();
+ return;
+ }
+
+ sta = peer->sta;
+ arsta = (struct ath11k_sta *)sta->drv_priv;
+
+ memset(&arsta->txrate, 0, sizeof(arsta->txrate));
+
+ switch (flags) {
+ case WMI_RATE_PREAMBLE_OFDM:
+ arsta->txrate.legacy = rate;
+ break;
+ case WMI_RATE_PREAMBLE_CCK:
+ arsta->txrate.legacy = rate;
+ break;
+ case WMI_RATE_PREAMBLE_HT:
+ arsta->txrate.mcs = mcs + 8 * (nss - 1);
+ arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
+ if (sgi)
+ arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ break;
+ case WMI_RATE_PREAMBLE_VHT:
+ arsta->txrate.mcs = mcs;
+ arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ if (sgi)
+ arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ break;
+ case WMI_RATE_PREAMBLE_HE:
+ arsta->txrate.mcs = mcs;
+ arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ arsta->txrate.he_dcm = dcm;
+ arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi);
+ arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(
+ (user_rate->ru_end -
+ user_rate->ru_start) + 1);
+ break;
+ }
+
+ arsta->txrate.nss = nss;
+ arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);
+ arsta->tx_duration += tx_duration;
+ memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info));
+
memset(peer_stats, 0, sizeof(*peer_stats));
peer_stats->succ_pkts = succ_pkts;
peer_stats->succ_bytes = succ_bytes;
@@ -1696,12 +1711,12 @@
if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar)))
ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);
+
+ spin_unlock_bh(&ab->base_lock);
+ rcu_read_unlock();
}
usr_stats->rate_stats_updated = true;
-
- spin_unlock_bh(&ab->base_lock);
- rcu_read_unlock();
}
static void ath11k_htt_update_ppdu_stats(struct ath11k *ar,
@@ -2354,16 +2369,27 @@
size_t hdr_len, crypto_len;
struct ieee80211_hdr *hdr;
u16 fc, qos_ctl = 0;
+ int expand_by;
u8 *crypto_hdr;
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype);
+ if (skb_headroom(msdu) < crypto_len) {
+ expand_by = crypto_len - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
crypto_hdr = skb_push(msdu, crypto_len);
ath11k_dp_rx_desc_get_crypto_header(ab, rx_desc, crypto_hdr, enctype);
}
fc = ath11k_dp_rxdesc_get_mpdu_frame_ctrl(ab, rx_desc);
hdr_len = ieee80211_hdrlen(fc);
+ if (skb_headroom(msdu) < hdr_len) {
+ expand_by = hdr_len - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
skb_push(msdu, hdr_len);
hdr = (struct ieee80211_hdr *)msdu->data;
hdr->frame_control = fc;
@@ -2399,6 +2425,7 @@
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
u16 qos_ctl = 0;
+ int expand_by = 0;
u8 *qos, *crypto_hdr;
bool add_qos_ctrl = false;
@@ -2443,6 +2470,11 @@
}
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ if (skb_headroom(msdu) < ath11k_dp_rx_crypto_param_len(ar, enctype)) {
+ expand_by = ath11k_dp_rx_crypto_param_len(ar, enctype) - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
if (first_hdr) {
memcpy(skb_push(msdu,
ath11k_dp_rx_crypto_param_len(ar, enctype)),
@@ -2456,13 +2488,28 @@
}
if (!rxcb->is_first_msdu || add_qos_ctrl) {
+ if (skb_headroom(msdu) < IEEE80211_QOS_CTL_LEN) {
+ expand_by = IEEE80211_QOS_CTL_LEN - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu,
IEEE80211_QOS_CTL_LEN), &qos_ctl,
IEEE80211_QOS_CTL_LEN);
+ if (skb_headroom(msdu) < hdr_len) {
+ expand_by = hdr_len - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu, hdr_len), decap_hdr, hdr_len);
return;
}
+ if (skb_headroom(msdu) < hdr_len) {
+ expand_by = hdr_len - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
/* original 802.11 header has a different DA and in
@@ -2571,6 +2618,7 @@
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
void *rfc1042;
+ int expand_by;
struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
struct ath11k_dp_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}};
@@ -2580,6 +2628,11 @@
ether_addr_copy(sa, eth->h_source);
rfc.snap_type = eth->h_proto;
skb_pull(msdu, sizeof(struct ethhdr));
+ if (skb_headroom(msdu) < sizeof(struct ath11k_dp_rfc1042_hdr)) {
+ expand_by = sizeof(struct ath11k_dp_rfc1042_hdr) - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), &rfc,
sizeof(struct ath11k_dp_rfc1042_hdr));
ath11k_get_dot11_hdr_from_rx_desc(ar, msdu, rxcb, status, enctype);
@@ -2597,6 +2650,11 @@
skb_pull(msdu, sizeof(struct ethhdr));
/* push rfc1042/llc/snap */
+ if (skb_headroom(msdu) < sizeof(struct ath11k_dp_rfc1042_hdr)) {
+ expand_by = sizeof(struct ath11k_dp_rfc1042_hdr) - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), rfc1042,
sizeof(struct ath11k_dp_rfc1042_hdr));
@@ -2605,12 +2663,22 @@
hdr_len = ieee80211_hdrlen(hdr->frame_control);
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ if (skb_headroom(msdu) < ath11k_dp_rx_crypto_param_len(ar, enctype)) {
+ expand_by = ath11k_dp_rx_crypto_param_len(ar, enctype) - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu,
ath11k_dp_rx_crypto_param_len(ar, enctype)),
(void *)hdr + hdr_len,
ath11k_dp_rx_crypto_param_len(ar, enctype));
}
+ if (skb_headroom(msdu) < hdr_len) {
+ expand_by = hdr_len - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
exit:
@@ -2631,6 +2699,7 @@
struct ieee80211_hdr *hdr;
size_t hdr_len;
u8 l3_pad_bytes;
+ int expand_by;
struct hal_rx_desc *rx_desc;
struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
@@ -2655,12 +2724,22 @@
hdr_len = ieee80211_hdrlen(hdr->frame_control);
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ if (skb_headroom(msdu) < ath11k_dp_rx_crypto_param_len(ar, enctype)) {
+ expand_by = ath11k_dp_rx_crypto_param_len(ar, enctype) - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu,
ath11k_dp_rx_crypto_param_len(ar, enctype)),
(void *)hdr + hdr_len,
ath11k_dp_rx_crypto_param_len(ar, enctype));
}
+ if (skb_headroom(msdu) < hdr_len) {
+ expand_by = hdr_len - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ return;
+ }
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
}
@@ -2689,11 +2768,14 @@
case DP_RX_DECAP_TYPE_ETHERNET2_DIX:
ehdr = (struct ethhdr *) msdu->data;
- /* mac80211 allows fast path only for authorized STA */
- if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) {
- ATH11K_SKB_RXCB(msdu)->is_eapol = true;
+ /* mac80211 allows fast path only for authorized STA and
+ also not supported for TKIP */
+ if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE) ||
+ enctype == HAL_ENCRYPT_TYPE_TKIP_MIC) {
+ ATH11K_SKB_RXCB(msdu)->is_eapol_tkip = true;
ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr,
enctype, status);
+ break;
}
/* PN for mcast packets will be validated in mac80211;
@@ -2824,6 +2906,9 @@
/* PN for multicast packets will be checked in mac80211 */
rxcb = ATH11K_SKB_RXCB(msdu);
+
+ rxcb->is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc);
+
fill_crypto_hdr = rxcb->is_mcbc;
if (rxcb->is_mcbc) {
rxcb->seq_no = ath11k_dp_rx_h_mpdu_start_seq_no(ar->ab, rx_desc);
@@ -3047,11 +3132,17 @@
u8 decap = DP_RX_DECAP_TYPE_RAW;
int len;
u8 index;
+ int expand_by;
bool is_mcbc = rxcb->is_mcbc;
- bool is_eapol = rxcb->is_eapol;
+ bool is_eapol_tkip = rxcb->is_eapol_tkip;
if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
!(status->flag & RX_FLAG_SKIP_MONITOR)) {
+ if (skb_headroom(msdu) < sizeof(known)) {
+ expand_by = sizeof(known) - skb_headroom(msdu);
+ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC)))
+ goto exit;
+ }
he = skb_push(msdu, sizeof(known));
memcpy(he, &known, sizeof(known));
status->flag |= RX_FLAG_RADIOTAP_HE;
@@ -3103,7 +3194,7 @@
* Also, fast_rx expectes the STA to be authorized, hence
* eapol packets are sent in slow path.
*/
- if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol &&
+ if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol_tkip &&
!(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) {
rx_status->flag |= RX_FLAG_8023;
goto exit;
@@ -3285,13 +3376,13 @@
int ret;
bool fast_rx;
LIST_HEAD(rx_list);
+ bool gro_support_enabled = ab->gro_support_enabled;
if (skb_queue_empty(msdu_list))
return;
rcu_read_lock();
-
ar = ab->pdevs[mac_id].ar;
if (unlikely(!rcu_dereference(ab->pdevs_active[mac_id]))) {
__skb_queue_purge(msdu_list);
@@ -3325,8 +3416,18 @@
ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status, &rx_list);
}
- if (!fast_rx)
- netif_receive_skb_list(&rx_list);
+ if (!fast_rx) {
+ struct sk_buff *skb, *tmp;
+
+ if (napi && gro_support_enabled) {
+ list_for_each_entry_safe(skb, tmp, &rx_list, list) {
+ skb_list_del_init(skb);
+ napi_gro_receive(napi, skb);
+ }
+ } else {
+ netif_receive_skb_list(&rx_list);
+ }
+ }
rcu_read_unlock();
}
@@ -3397,6 +3498,17 @@
ath11k_hal_srng_access_begin(ab, srng);
while (likely(desc = (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, srng))) {
+
+ push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON,
+ desc->info0);
+
+ if (unlikely(push_reason ==
+ HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED)) {
+ ath11k_warn(ab,"Received invalid desc\n");
+ ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++;
+ continue;
+ }
+
cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE,
desc->buf_addr_info.info1);
buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID,
@@ -3429,8 +3541,6 @@
num_buffs_reaped[mac_id]++;
- push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON,
- desc->info0);
if (unlikely(push_reason !=
HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) {
dev_kfree_skb_any(msdu);
@@ -3569,12 +3679,15 @@
{
struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats;
u32 num_msdu;
+ u32 bw_offset;
if (!rx_stats)
return;
+ arsta->last_tx_pkt_bw = ppdu_info->bw;
+ bw_offset = arsta->last_tx_pkt_bw * 3;
arsta->rssi_comb = ppdu_info->rssi_comb;
- ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb);
+ ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb + bw_offset);
if (!ath11k_debugfs_is_extd_rx_stats_enabled(ar))
return;
@@ -3974,6 +4087,14 @@
return DP_MON_STATUS_NO_DMA;
rxcb = ATH11K_SKB_RXCB(skb);
+
+ if (!rxcb->paddr) {
+ ath11k_warn(ab, "Invalid paddr at SKB cb \n");
+ } else if (paddr != rxcb->paddr) {
+ ath11k_warn(ab, "Invalid desc and skb fetch %pad rxcb %pad rbm %u cookie %u\n",
+ &paddr, &(rxcb->paddr), rbm, cookie);
+ }
+
dma_sync_single_for_cpu(ab->dev, rxcb->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
@@ -4017,6 +4138,7 @@
ath11k_hal_srng_access_begin(ab, srng);
while (*budget) {
*budget -= 1;
+ ar->mon_status_no_buf_done = MON_STATUS_BUF_DONE;
rx_mon_status_desc = ath11k_hal_srng_src_peek(ab, srng);
if (!rx_mon_status_desc) {
pmon->mon_status_buf_state = DP_MON_STATUS_REPLINISH;
@@ -4046,6 +4168,18 @@
rxcb = ATH11K_SKB_RXCB(skb);
+ if (ar->mon_status_skb_zero) {
+ ath11k_warn(ab, "Previous mon status skb zero \n");
+ ar->mon_status_skb_zero = false;
+ }
+
+ if (!rxcb->paddr) {
+ ath11k_warn(ab, "NULL paddr at SKB cb \n");
+ } else if (paddr != rxcb->paddr) {
+ ath11k_warn(ab, "paddr mismatch desc %pad rxcb %pad rbm-%u cookie %u\n",
+ &paddr, &(rxcb->paddr), rbm, cookie);
+ }
+
dma_sync_single_for_cpu(ab->dev, rxcb->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
@@ -4054,6 +4188,7 @@
if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) !=
HAL_RX_STATUS_BUFFER_DONE) {
+ ar->mon_status_no_buf_done = MON_STATUS_NO_BUF_DONE;
/* RxDMA status done bit might not be set even
* though tp is moved by HW.
*/
@@ -4075,6 +4210,7 @@
if (reap_status == DP_MON_STATUS_NO_DMA) {
continue;
} else if (reap_status == DP_MON_STATUS_REPLINISH) {
+ ar->mon_status_no_buf_done = MON_STATUS_BUF_DONE_NEXT;
ath11k_warn(ab, "mon status DONE not set %lx, buf_id %d\n",
FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl),
buf_id);
@@ -4117,6 +4253,9 @@
&buf_id);
if (!skb) {
+ ath11k_warn(ab, "failed to allocate skb no buf done %d\n",
+ ar->mon_status_no_buf_done);
+ ar->mon_status_skb_zero = true;
ath11k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0,
HAL_RX_BUF_RBM_SW3_BM);
num_buffs_reaped++;
@@ -5148,8 +5287,6 @@
struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
bool drop = false;
- ar->ab->soc_stats.reo_error[rxcb->err_code]++;
-
switch (rxcb->err_code) {
case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO:
if (ath11k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list))
@@ -5169,10 +5306,15 @@
break;
}
+ if (drop)
+ ar->ab->soc_stats.reo_error_drop[rxcb->err_code]++;
+ else
+ ar->ab->soc_stats.reo_error[rxcb->err_code]++;
+
return drop;
}
-static void ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu,
+static bool ath11k_dp_rx_h_tkip_mic_err(struct ath11k *ar, struct sk_buff *msdu,
struct ieee80211_rx_status *status)
{
u16 msdu_len;
@@ -5186,6 +5328,14 @@
l3pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, desc);
msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(ar->ab, desc);
+
+ if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) {
+ ath11k_warn(ar->ab, "invalid msdu len in tkip mirc err %u\n", msdu_len);
+ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "", desc,
+ sizeof(struct hal_rx_desc));
+ return true;
+ }
+
skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len);
skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes);
@@ -5196,6 +5346,8 @@
ath11k_dp_rx_h_undecap(ar, msdu, desc,
HAL_ENCRYPT_TYPE_TKIP_MIC, status, false);
+
+ return false;
}
static bool ath11k_dp_rx_h_rxdma_err(struct ath11k *ar, struct sk_buff *msdu,
@@ -5208,7 +5360,7 @@
switch (rxcb->err_code) {
case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR:
- ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status);
+ drop = ath11k_dp_rx_h_tkip_mic_err(ar, msdu, status);
break;
default:
/* TODO: Review other rxdma error code to check if anything is
diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c
index bb64b76..d654f92 100644
--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
@@ -392,6 +392,8 @@
/* TODO: Take care of other encap modes as well */
ret = -EINVAL;
atomic_inc(&ab->soc_stats.tx_err.misc_fail);
+ arsta->drop_pkts++;
+ arsta->drop_bytes += skb->len;
goto fail_remove_idr;
}
@@ -831,6 +833,7 @@
status.info = info;
rate = arsta->last_txrate;
status.rate = &rate;
+ arsta->tx_retry_count += ts.try_cnt > 1 ? (ts.try_cnt - 1) : 0;
if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar))) {
if(arsta->wbm_tx_stats && wbm_status < HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX)
@@ -838,10 +841,19 @@
}
if (ts.status != HAL_WBM_TQM_REL_REASON_FRAME_ACKED) {
- arsta->fail_pkts += 1;
- arsta->per_fail_pkts += 1;
- arsta->fail_bytes += msdu->len;
- arsta->ber_fail_bytes += msdu->len;
+ if (ts.status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_MPDU) {
+ arsta->drop_pkts += 1;
+ arsta->drop_bytes += msdu->len;
+ spin_unlock_bh(&ab->base_lock);
+ dev_kfree_skb_any(msdu);
+ goto exit;
+ } else {
+ arsta->fail_pkts += 1;
+ arsta->per_fail_pkts += 1;
+ arsta->fail_bytes += msdu->len;
+ arsta->ber_fail_bytes += msdu->len;
+ }
+
if(arsta->per_fail_pkts + arsta->per_succ_pkts >=
ATH11K_NUM_PKTS_THRSHLD_FOR_PER)
ath11k_sta_stats_update_per(arsta);
@@ -895,8 +907,7 @@
if (FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0) ==
HAL_WBM_REL_SRC_MODULE_FW) {
- status_desc = (struct htt_tx_wbm_completion *)((u8 *)desc) + HTT_TX_WBM_COMP_STATUS_OFFSET;
-
+ status_desc = (struct htt_tx_wbm_completion *)(((u8 *)desc) + HTT_TX_WBM_COMP_STATUS_OFFSET);
/* Dont consider HTT_TX_COMP_STATUS_MEC_NOTIFY */
if (FIELD_GET(HTT_TX_WBM_COMP_INFO0_STATUS, status_desc->info0) ==
HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY)
@@ -1083,10 +1094,6 @@
if (cmd_num == 0)
return -EINVAL;
- /* Trigger reo status polling if required */
- if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS)
- ath11k_dp_start_reo_status_timer(ab);
-
if (!cb)
return 0;
diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h
index 875efcb..2ef92cd 100644
--- a/drivers/net/wireless/ath/ath11k/hal.h
+++ b/drivers/net/wireless/ath/ath11k/hal.h
@@ -21,7 +21,8 @@
#define HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC 12
#define HAL_MAX_AVAIL_BLK_RES 3
-#define HAL_RING_BASE_ALIGN 8
+#define HAL_RING_BASE_ALIGN 32
+#define HAL_RXDMA_DIR_BUF_RING_BASE_ALIGN 8
#define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704
/* TODO: Check with hw team on the supported scatter buf size */
diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c
index 204d2e1..9741ccf 100644
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -1450,39 +1450,6 @@
},
};
-const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq5018 = {
- .tx = {
- ATH11K_TX_RING_MASK_0,
- ATH11K_TX_RING_MASK_1,
- ATH11K_TX_RING_MASK_2,
- },
- .rx_mon_status = {
- 0, 0, 0,
- ATH11K_RX_MON_STATUS_RING_MASK_0,
- },
- .rx = {
- 0, 0, 0, 0,
- ATH11K_RX_RING_MASK_0,
- ATH11K_RX_RING_MASK_1,
- ATH11K_RX_RING_MASK_2,
- ATH11K_RX_RING_MASK_3,
- },
- .rx_err = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- ATH11K_RX_ERR_RING_MASK_0,
- },
- .rx_wbm_rel = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0,
- ATH11K_RX_WBM_REL_RING_MASK_0,
- },
- .rxdma2host = {
- ATH11K_RXDMA2HOST_RING_MASK_0,
- },
- .host2rxdma = {
- ATH11K_HOST2RXDMA_RING_MASK_0,
- },
-};
-
/* Target firmware's Copy Engine configuration. */
const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq8074[] = {
/* CE0: host->target HTC control and raw streams */
@@ -2197,6 +2164,43 @@
},
};
+const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122 = {
+ .tx = {
+ ATH11K_TX_RING_MASK_0,
+ ATH11K_TX_RING_MASK_1,
+ ATH11K_TX_RING_MASK_2,
+ },
+ .rx_mon_status = {
+ 0, 0, 0,
+ ATH11K_RX_MON_STATUS_RING_MASK_0,
+ },
+ .rx = {
+ 0, 0, 0, 0,
+ ATH11K_RX_RING_MASK_0,
+ ATH11K_RX_RING_MASK_1,
+ ATH11K_RX_RING_MASK_2,
+ ATH11K_RX_RING_MASK_3,
+ },
+ .rx_err = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ ATH11K_RX_ERR_RING_MASK_0,
+ },
+ .rx_wbm_rel = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ATH11K_RX_WBM_REL_RING_MASK_0,
+ },
+ .reo_status = {
+ 0, 0, 0,
+ ATH11K_REO_STATUS_RING_MASK_0,
+ },
+ .rxdma2host = {
+ ATH11K_RXDMA2HOST_RING_MASK_0,
+ },
+ .host2rxdma = {
+ ATH11K_HOST2RXDMA_RING_MASK_0,
+ },
+};
+
/* Target firmware's Copy Engine configuration for IPQ5018 */
const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[] = {
/* CE0: host->target HTC control and raw streams */
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index bff8ea2..5a34b3b 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -291,7 +291,7 @@
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074;
-extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq5018;
+extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122;
static inline
int ath11k_hw_get_mac_from_pdev_id(struct ath11k_hw_params *hw,
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index dd9825d..2203e83 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -615,6 +615,7 @@
ar->txpower_limit_2g = ar->max_tx_power;
ar->txpower_limit_5g = ar->max_tx_power;
+ ar->txpower_limit_6g = ar->max_tx_power;
ar->txpower_scale = WMI_HOST_TP_SCALE_MAX;
}
@@ -785,6 +786,16 @@
ar->txpower_limit_5g = txpower;
}
+ if ((ar->hw->wiphy->bands[NL80211_BAND_6GHZ]) &&
+ ar->txpower_limit_6g != txpower) {
+ param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G;
+ ret = ath11k_wmi_pdev_set_param(ar, param,
+ txpower, ar->pdev->pdev_id);
+ if (ret)
+ goto fail;
+ ar->txpower_limit_6g = txpower;
+ }
+
return 0;
fail:
@@ -887,6 +898,7 @@
spin_lock_bh(&ab->base_lock);
list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
ath11k_peer_rx_tid_cleanup(ar, peer);
+ ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list);
kfree(peer);
}
@@ -1229,6 +1241,7 @@
u8 *ies;
int ret;
const u8 *vht_cap_ie;
+ u64 adjusted_tsf;
ies = bcn->data + ieee80211_get_hdrlen_from_skb(bcn);
ies += sizeof(mgmt->u.beacon);
@@ -1266,6 +1279,15 @@
WMI_BEACON_EMA_PARAM_LAST_TMPL_SHIFT;
}
+ /* Make the TSF offset negative so beacons in the same
+ * staggered batch have the same TSF.
+ */
+ if (arvif->tbtt_offset) {
+ adjusted_tsf = cpu_to_le64(0ULL - arvif->tbtt_offset);
+ mgmt = (void *)bcn->data;
+ memcpy(&mgmt->u.beacon.timestamp, &adjusted_tsf, sizeof(adjusted_tsf));
+ }
+
ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, ema_param);
if (ret)
@@ -1324,7 +1346,7 @@
return ret;
}
-static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
+int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
{
if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
return 0;
@@ -3183,7 +3205,7 @@
static void ath11k_mac_op_nss_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- u32 changed)
+ u64 changed)
{
struct ath11k *ar = hw->priv;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
@@ -3221,7 +3243,7 @@
static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
- u32 changed)
+ u64 changed)
{
struct ath11k *ar = hw->priv;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
@@ -3239,6 +3261,24 @@
mutex_lock(&ar->conf_mutex);
+ if (changed & BSS_CHANGED_6G_POWER_MODE) {
+ if (ar->supports_6ghz &&
+ info->chandef.chan->band == NL80211_BAND_6GHZ &&
+ arvif->vdev_type == WMI_VDEV_TYPE_AP &&
+ test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map)) {
+ ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx);
+ ret = ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
+ &arvif->reg_tpc_info);
+ if (ret)
+ ath11k_warn(ar->ab, "Failed to set 6GHZ power mode\n");
+ else
+ ieee80211_6ghz_power_mode_switch_done(arvif->vif);
+
+ } else {
+ ath11k_warn(ar->ab, "Set 6GHZ power mode not applicable\n");
+ }
+ }
+
if (changed & BSS_CHANGED_FTM_RESPONDER &&
arvif->ftm_responder != info->ftm_responder &&
(vif->type == NL80211_IFTYPE_AP ||
@@ -3433,19 +3473,8 @@
if (changed & BSS_CHANGED_TXPOWER) {
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev_id %i txpower %d\n",
arvif->vdev_id, info->txpower);
-
- if (ar->supports_6ghz && info->chandef.chan &&
- info->chandef.chan->band == NL80211_BAND_6GHZ &&
- (arvif->vdev_type == WMI_VDEV_TYPE_STA ||
- arvif->vdev_type == WMI_VDEV_TYPE_AP) &&
- test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT,
- ar->ab->wmi_ab.svc_map)) {
- ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
- "discard tx power, change to set TPC power\n");
- } else {
- arvif->txpower = info->txpower;
- ath11k_mac_txpower_recalc(ar);
- }
+ arvif->txpower = info->txpower;
+ ath11k_mac_txpower_recalc(ar);
}
if (changed & BSS_CHANGED_MCAST_RATE &&
@@ -4031,6 +4060,7 @@
const u8 *peer_addr;
int ret = 0;
u32 flags = 0;
+ unsigned long time_left;
/* BIP needs to be done in software */
if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
@@ -4108,6 +4138,14 @@
else
flags |= WMI_KEY_GROUP;
+ if (arsta && cmd == DISABLE_KEY && arsta->tx_disassoc) {
+ time_left = wait_for_completion_timeout(&arsta->disassoc_comp,
+ ATH11K_DISASSOC_TX_COMPLETION_TIMEOUT);
+ if (!time_left)
+ ath11k_warn(ab, "disassociation tx completion timeout %pM\n",
+ sta->addr);
+ }
+
ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags);
if (ret) {
ath11k_warn(ab, "ath11k_install_key failed (%d)\n", ret);
@@ -4759,8 +4797,10 @@
struct ieee80211_sta *sta)
{
struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
bool peer_dbg_info;
int ret = 0;
+ unsigned long time_left;
lockdep_assert_held(&ar->conf_mutex);
@@ -4776,6 +4816,14 @@
return ret;
}
+ if (arsta->tx_disassoc) {
+ time_left = wait_for_completion_timeout(&arsta->disassoc_comp,
+ ATH11K_DISASSOC_TX_COMPLETION_TIMEOUT);
+ if (!time_left)
+ ath11k_warn(ar->ab, "disassociation tx completion timeout sta %pM\n",
+ sta->addr);
+ }
+
ret = ath11k_clear_peer_keys(arvif, sta->addr);
if (ret) {
ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n",
@@ -4796,11 +4844,12 @@
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
- u32 changed, bw, nss, smps;
+ u32 changed, bw, nss, smps, bw_last;
int err, num_ht_rates, num_vht_rates, num_he_rates;
const struct cfg80211_bitrate_mask *mask;
struct peer_assoc_params peer_arg;
bool peer_dbg_info, debug;
+ enum wmi_phy_mode peer_phymode;
arsta = container_of(wk, struct ath11k_sta, update_wk);
sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
@@ -4821,6 +4870,7 @@
arsta->changed = 0;
bw = arsta->bw;
+ bw_last = arsta->bw_last;
nss = arsta->nss;
smps = arsta->smps;
@@ -4836,11 +4886,59 @@
ath11k_mac_max_he_nss(he_mcs_mask)));
if (changed & IEEE80211_RC_BW_CHANGED) {
- err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
- WMI_PEER_CHWIDTH, bw);
- if (err)
- ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n",
- sta->addr, bw, err);
+ /* Get the the peer phymode */
+ ath11k_peer_assoc_h_phymode(ar, arvif->vif, sta, &peer_arg);
+ peer_phymode = peer_arg.peer_phymode;
+
+ if (peer_dbg_info)
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "mac update sta %pM peer bw %d phymode %d\n",
+ sta->addr, bw, peer_phymode);
+
+ if (bw > bw_last) {
+ /* BW is upgraded. In this case we send WMI_PEER_PHYMODE
+ * followed by WMI_PEER_CHWIDTH
+ */
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW upgrade for sta %pM new BW %d, old BW %d\n",
+ sta->addr, bw, bw_last);
+
+ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
+ WMI_PEER_PHYMODE, peer_phymode);
+
+ if (err) {
+ ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n",
+ sta->addr, peer_phymode, err);
+ goto err_rc_update;
+ }
+
+ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
+ WMI_PEER_CHWIDTH, bw);
+
+ if (err)
+ ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n",
+ sta->addr, bw, err);
+ } else {
+ /* BW is downgraded. In this case we send WMI_PEER_CHWIDTH
+ * followed by WMI_PEER_PHYMODE
+ */
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW downgrade for sta %pM new BW %d, old BW %d\n",
+ sta->addr, bw, bw_last);
+
+ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
+ WMI_PEER_CHWIDTH, bw);
+
+ if (err) {
+ ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n",
+ sta->addr, bw, err);
+ goto err_rc_update;
+ }
+
+ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
+ WMI_PEER_PHYMODE, peer_phymode);
+
+ if (err)
+ ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n",
+ sta->addr, peer_phymode, err);
+ }
}
if (changed & IEEE80211_RC_NSS_CHANGED) {
@@ -4931,6 +5029,7 @@
}
}
+err_rc_update:
mutex_unlock(&ar->conf_mutex);
}
@@ -5497,6 +5596,8 @@
INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
INIT_WORK(&arsta->use_4addr_wk, ath11k_sta_use_4addr_wk);
INIT_WORK(&arsta->tid_config_wk, ath11k_sta_tid_cfg_wk);
+ init_completion(&arsta->disassoc_comp);
+ arsta->tx_disassoc = false;
ret = ath11k_mac_station_add(ar, vif, sta);
if (ret)
@@ -5520,13 +5621,13 @@
if (peer && peer->sta == sta) {
ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
vif->addr, arvif->vdev_id);
+ ath11k_peer_rhash_delete(ar->ab, peer);
peer->sta = NULL;
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
}
spin_unlock_bh(&ar->ab->base_lock);
-
kfree(arsta->tx_stats);
arsta->tx_stats = NULL;
kfree(arsta->wbm_tx_stats);
@@ -5563,6 +5664,11 @@
peer->is_authorized = true;
spin_unlock_bh(&ar->ab->base_lock);
+ spin_lock_bh(&ar->data_lock);
+ /* Set arsta bw and last bw */
+ arsta->bw = arsta->bw_last = sta->bandwidth;
+ spin_unlock_bh(&ar->data_lock);
+
if (vif->type == NL80211_IFTYPE_STATION) {
ret = ath11k_wmi_set_peer_param(ar, sta->addr,
arvif->vdev_id,
@@ -5725,6 +5831,7 @@
break;
}
+ arsta->bw_last = arsta->bw;
arsta->bw = bw;
}
@@ -6501,6 +6608,8 @@
struct ath11k_base *ab = ar->ab;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info;
+ struct ath11k_peer *peer;
+ struct ath11k_sta *arsta;
dma_addr_t paddr;
bool tx_params_valid = false;
int buf_id;
@@ -6549,6 +6658,18 @@
peer_is_in_cfr_unassoc_pool(ar, hdr->addr1))
tx_params_valid = true;
+ if (ieee80211_has_protected(hdr->frame_control) &&
+ ieee80211_is_disassoc(hdr->frame_control)) {
+ spin_lock_bh(&ar->ab->base_lock);
+ peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr1);
+ if (peer && peer->sta) {
+ arsta = (struct ath11k_sta *)peer->sta->drv_priv;
+ reinit_completion(&arsta->disassoc_comp);
+ arsta->tx_disassoc = true;
+ }
+ spin_unlock_bh(&ar->ab->base_lock);
+ }
+
ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb,
tx_params_valid);
if (ret) {
@@ -6666,11 +6787,13 @@
u32 info_flags = info->flags;
struct ieee80211_sta *sta = control->sta;
struct ath11k_sta *arsta = NULL;
+ struct ieee80211_mgmt *mgmt;
bool is_prb_rsp;
u16 frm_type = 0;
u8 tid, *qos_ctl;
bool noack = false;
int ret;
+ u64 adjusted_tsf;
if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))) {
ieee80211_free_txskb(ar->hw, skb);
@@ -6690,6 +6813,12 @@
} else if (ieee80211_is_mgmt(hdr->frame_control)) {
frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control);
is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
+ if (is_prb_rsp && arvif->tbtt_offset) {
+ mgmt = (struct ieee80211_mgmt *)skb->data;
+ adjusted_tsf = cpu_to_le64(0ULL - arvif->tbtt_offset);
+ memcpy(&mgmt->u.probe_resp.timestamp, &adjusted_tsf,
+ sizeof(adjusted_tsf));
+ }
ret = ath11k_mac_tx_over_wmi(ar, skb, is_prb_rsp);
if (ret) {
if (ret != -EBUSY)
@@ -6985,6 +7114,22 @@
return ret;
}
+static void ath11k_update_bcn_template_work(struct work_struct *work)
+{
+ struct ath11k_vif *arvif = container_of(work, struct ath11k_vif,
+ update_bcn_template_work);
+ struct ath11k *ar = arvif->ar;
+ int ret = 0;
+
+ mutex_lock(&ar->conf_mutex);
+ if (arvif->is_up)
+ ret = ath11k_mac_setup_bcn_tmpl(arvif);
+ mutex_unlock(&ar->conf_mutex);
+ if (ret)
+ ath11k_warn(ar->ab, "failed to submit beacon template for vdev_id : %d ret : %d\n",
+ arvif->vdev_id, ret);
+}
+
static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
{
struct ath11k *ar = hw->priv;
@@ -7207,6 +7352,7 @@
INIT_DELAYED_WORK(&arvif->connection_loss_work,
ath11k_mac_vif_sta_connection_loss_work);
+ INIT_WORK(&arvif->update_bcn_template_work, ath11k_update_bcn_template_work);
for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
arvif->bitrate_mask.control[i].legacy = 0xffffffff;
@@ -7238,8 +7384,12 @@
goto err;
}
- ath11k_debugfs_dbg_mac_filter(arvif);
- ath11k_debugfs_wbm_tx_comp_stats(arvif);
+ if (ar->state != ATH11K_STATE_RESTARTED) {
+ ath11k_debugfs_dbg_mac_filter(arvif);
+ ath11k_debugfs_wbm_tx_comp_stats(arvif);
+ } else {
+ INIT_LIST_HEAD(&arvif->mac_filters);
+ }
switch (vif->type) {
case NL80211_IFTYPE_UNSPECIFIED:
@@ -7408,6 +7558,14 @@
param_id = WMI_VDEV_PARAM_RTS_THRESHOLD;
param_value = ar->hw->wiphy->rts_threshold;
+
+ if (vif->type == NL80211_IFTYPE_MESH_POINT &&
+ !ar->rts_threshold[vif->type])
+ ar->rts_threshold[vif->type] = 1;
+
+ if (ar->rts_threshold[vif->type])
+ param_value = ar->rts_threshold[vif->type];
+
ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param_id, param_value);
if (ret) {
@@ -7429,8 +7587,13 @@
goto err_peer_del;
}
- ath11k_debug_aggr_size_config_init(arvif);
- ath11k_debugfs_wmi_ctrl_stats(arvif);
+ if (ar->state != ATH11K_STATE_RESTARTED) {
+ ath11k_debug_aggr_size_config_init(arvif);
+ ath11k_debugfs_wmi_ctrl_stats(arvif);
+ } else {
+ INIT_LIST_HEAD(&arvif->ar->debug.wmi_list);
+ init_completion(&arvif->ar->debug.wmi_ctrl_path_stats_rcvd);
+ }
mutex_unlock(&ar->conf_mutex);
@@ -7508,6 +7671,7 @@
}
cancel_delayed_work_sync(&arvif->connection_loss_work);
+ cancel_work_sync(&arvif->update_bcn_template_work);
ath11k_dbg(ab, ATH11K_DBG_MAC, "mac remove interface (vdev %d)\n",
arvif->vdev_id);
@@ -8532,7 +8696,7 @@
bool is_psd_power = false, is_tpe_present = false;
s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
psd_power, tx_power = 0, eirp_power = 0;
- u16 oper_freq = 0, start_freq = 0, center_freq = 0;
+ u16 oper_freq = 0, start_freq = 0, center_freq = 0, op_center_freq = 0;
u8 reg_6g_power_mode;
enum nl80211_chan_width bw;
int cfi;
@@ -8658,8 +8822,8 @@
psd_power = temp_chan->psd;
if (reg_6g_power_mode == IEEE80211_REG_SP_AP &&
ar->afc.is_6g_afc_power_event_received) {
- center_freq = ctx->def.center_freq1;
- cfi = ieee80211_frequency_to_channel(center_freq);
+ op_center_freq = ctx->def.center_freq1;
+ cfi = ieee80211_frequency_to_channel(op_center_freq);
bw = ctx->def.width;
eirp_power = ath11k_reg_get_afc_eirp_power(ar,
bw,
@@ -10448,6 +10612,7 @@
struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ath11k *ar = hw->priv;
+ u32 bw_offset = 0;
sinfo->rx_duration = arsta->rx_duration;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
@@ -10460,6 +10625,11 @@
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RTS_RETRIES);
}
+ if (arsta->tx_retry_count) {
+ sinfo->tx_retries = arsta->tx_retry_count;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+ }
+
if (!arsta->txrate.legacy && !arsta->txrate.nss)
return;
@@ -10476,11 +10646,11 @@
sinfo->txrate.flags = arsta->txrate.flags;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
- /* TODO: Use real NF instead of default one. */
- sinfo->signal = arsta->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR;
+ bw_offset = arsta->last_tx_pkt_bw * 3;
+ sinfo->signal = arsta->rssi_comb + ar->chan_noise_floor + bw_offset;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
- sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi) + ATH11K_DEFAULT_NOISE_FLOOR;
+ sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi) + ar->chan_noise_floor;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
if (ath11k_nss_offload_enabled(ar->ab))
@@ -10629,7 +10799,7 @@
static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- int *dbm)
+ int *mbm)
{
struct ath11k *ar = hw->priv;
struct ath11k_base *ab = ar->ab;
@@ -10668,22 +10838,134 @@
}
/* tx power is set as 2 units per dBm in FW. */
- *dbm = pdev->chan_tx_power/2;
+ *mbm = DBM_TO_MBM(pdev->chan_tx_power) / 2;
spin_unlock_bh(&ar->data_lock);
mutex_unlock(&ar->conf_mutex);
- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "%s: txpower: %d from fw\n", __func__, *dbm);
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "%s: txpower: %d from fw\n", __func__, *mbm);
return 0;
err_unlock:
mutex_unlock(&ar->conf_mutex);
/* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
- *dbm = vif->bss_conf.txpower;
+ *mbm = DBM_TO_MBM(vif->bss_conf.txpower);
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "%s: txpower: %d from bss_conf\n", __func__, vif->bss_conf.txpower);
return 0;
}
+static int ath11k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath11k *ar = hw->priv;
+
+ mutex_lock(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+ ar->scan.roc_notify = false;
+ spin_unlock_bh(&ar->data_lock);
+
+ ath11k_scan_abort(ar);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ cancel_delayed_work_sync(&ar->scan.timeout);
+
+ return 0;
+}
+
+static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan,
+ int duration,
+ enum ieee80211_roc_type type)
+{
+ struct ath11k *ar = hw->priv;
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct scan_req_params *arg;
+ int ret;
+ u32 scan_time_msec;
+
+ mutex_lock(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+ switch (ar->scan.state) {
+ case ATH11K_SCAN_IDLE:
+ reinit_completion(&ar->scan.started);
+ reinit_completion(&ar->scan.completed);
+ reinit_completion(&ar->scan.on_channel);
+ ar->scan.state = ATH11K_SCAN_STARTING;
+ ar->scan.is_roc = true;
+ ar->scan.vdev_id = arvif->vdev_id;
+ ar->scan.roc_freq = chan->center_freq;
+ ar->scan.roc_notify = true;
+ ret = 0;
+ break;
+ case ATH11K_SCAN_STARTING:
+ case ATH11K_SCAN_RUNNING:
+ case ATH11K_SCAN_ABORTING:
+ ret = -EBUSY;
+ break;
+ }
+ spin_unlock_bh(&ar->data_lock);
+
+ if (ret)
+ goto exit;
+
+ scan_time_msec = ar->hw->wiphy->max_remain_on_channel_duration * 2;
+
+ arg = kzalloc(sizeof(*arg), GFP_KERNEL);
+ if (!arg) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ath11k_wmi_start_scan_init(ar, arg);
+ arg->chan_list.num_chan = 1;
+
+ arg->vdev_id = arvif->vdev_id;
+ arg->scan_id = ATH11K_SCAN_ID;
+ arg->chan_list.chan[0].freq = chan->center_freq;
+ arg->dwell_time_active = scan_time_msec;
+ arg->dwell_time_passive = scan_time_msec;
+ arg->max_scan_time = scan_time_msec;
+ arg->scan_flags |= WMI_SCAN_FLAG_PASSIVE;
+ arg->scan_flags |= WMI_SCAN_FILTER_PROBE_REQ;
+ arg->burst_duration = duration;
+
+ ret = ath11k_start_scan(ar, arg);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to start roc scan: %d\n", ret);
+
+ spin_lock_bh(&ar->data_lock);
+ ar->scan.state = ATH11K_SCAN_IDLE;
+ spin_unlock_bh(&ar->data_lock);
+ goto exit;
+ }
+
+ ret = wait_for_completion_timeout(&ar->scan.on_channel, 3 * HZ);
+ if (ret == 0) {
+ ath11k_warn(ar->ab, "failed to switch to channel for roc scan\n");
+ ret = ath11k_scan_stop(ar);
+ if (ret)
+ ath11k_warn(ar->ab, "failed to stop scan: %d\n", ret);
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+
+ ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
+ msecs_to_jiffies(duration));
+
+ ret = 0;
+
+exit:
+ if (arg)
+ kfree(arg);
+
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
static const struct ieee80211_ops ath11k_ops = {
.tx = ath11k_mac_op_tx,
.start = ath11k_mac_op_start,
@@ -10722,6 +11004,8 @@
.set_tid_config = ath11k_mac_op_set_tid_config,
.reset_tid_config = ath11k_mac_op_reset_tid_config,
.sta_set_mgmt_rts_cts = ath11k_mac_op_sta_set_mgmt_rts_cts,
+ .remain_on_channel = ath11k_mac_op_remain_on_channel,
+ .cancel_remain_on_channel = ath11k_mac_op_cancel_remain_on_channel,
CFG80211_TESTMODE_CMD(ath11k_tm_cmd)
#ifdef CONFIG_ATH11K_DEBUGFS
.sta_add_debugfs = ath11k_debugfs_sta_op_add,
@@ -11113,6 +11397,8 @@
__ath11k_mac_unregister(ar);
}
+
+ ath11k_peer_rhash_tbl_destroy(ab);
}
static int __ath11k_mac_register(struct ath11k *ar)
@@ -11391,6 +11677,10 @@
ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1;
+ ret = ath11k_peer_rhash_tbl_init(ab);
+ if (ret)
+ return ret;
+
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
@@ -11421,6 +11711,8 @@
__ath11k_mac_unregister(ar);
}
+ ath11k_peer_rhash_tbl_destroy(ab);
+
return ret;
}
@@ -11487,6 +11779,7 @@
init_completion(&ar->bss_survey_done);
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
+ init_completion(&ar->scan.on_channel);
init_completion(&ar->thermal.wmi_sync);
INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work);
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index 7076e56..475756a 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -133,6 +133,8 @@
#define IEEE80211_HE_DL_MU_SUPPORT_ENABLE 2
#define IEEE80211_HE_DL_MU_SUPPORT_INVALID 3
+#define ATH11K_DISASSOC_TX_COMPLETION_TIMEOUT (3 * HZ)
+
extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default;
int ath11k_mac_ap_ps_recalc(struct ath11k *ar);
@@ -174,4 +176,5 @@
void ath11k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *conf,
void *data);
+int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index a951b7d..d17dff5 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -937,7 +937,7 @@
for (j = 0; j < irq_grp->num_irq; j++) {
int irq_idx = irq_grp->irqs[j];
- int vector = (i % num_vectors) + base_vector;
+ int vector = (i % num_vectors);
irq_set_status_flags(msi_desc->irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_irq(&pdev->dev, msi_desc->irq,
diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c
index 81d3df4..b0cc6f8 100644
--- a/drivers/net/wireless/ath/ath11k/peer.c
+++ b/drivers/net/wireless/ath/ath11k/peer.c
@@ -27,25 +27,6 @@
return NULL;
}
-static struct ath11k_peer *ath11k_peer_find_by_pdev_idx(struct ath11k_base *ab,
- u8 pdev_idx, const u8 *addr)
-{
- struct ath11k_peer *peer;
-
- lockdep_assert_held(&ab->base_lock);
-
- list_for_each_entry(peer, &ab->peers, list) {
- if (peer->pdev_idx != pdev_idx)
- continue;
- if (!ether_addr_equal(peer->addr, addr))
- continue;
-
- return peer;
- }
-
- return NULL;
-}
-
struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab,
const u8 *addr)
{
@@ -53,14 +34,13 @@
lockdep_assert_held(&ab->base_lock);
- list_for_each_entry(peer, &ab->peers, list) {
- if (!ether_addr_equal(peer->addr, addr))
- continue;
+ if (!ab->rhead_peer_addr)
+ return NULL;
- return peer;
- }
+ peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr,
+ ab->rhash_peer_addr_param);
- return NULL;
+ return peer;
}
struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,
@@ -70,11 +50,13 @@
lockdep_assert_held(&ab->base_lock);
- list_for_each_entry(peer, &ab->peers, list)
- if (peer_id == peer->peer_id)
- return peer;
+ if (!ab->rhead_peer_id)
+ return NULL;
- return NULL;
+ peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id,
+ ab->rhash_peer_id_param);
+
+ return peer;
}
struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
@@ -461,6 +443,7 @@
ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, peer_id);
+ ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list);
kfree(peer);
wake_up(&ab->peer_mapping_wq);
@@ -502,6 +485,7 @@
ar->bss_peer = NULL;
free_peer:
rcu_read_unlock();
+ ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list);
kfree(peer);
wake_up(&ab->peer_mapping_wq);
@@ -607,6 +591,69 @@
return 0;
}
+static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab,
+ struct rhashtable *rtbl,
+ struct rhash_head *rhead,
+ struct rhashtable_params *params,
+ void *key)
+{
+ struct ath11k_peer *tmp;
+
+ lockdep_assert_held(&ab->base_lock);
+
+ tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params);
+
+ if (!tmp)
+ return 0;
+ else if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ else
+ return -EEXIST;
+}
+
+static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab,
+ struct rhashtable *rtbl,
+ struct rhash_head *rhead,
+ struct rhashtable_params *params)
+{
+ lockdep_assert_held(&ab->base_lock);
+
+ return rhashtable_remove_fast(rtbl, rhead, *params);
+}
+
+static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer)
+{
+ int ret;
+
+ lockdep_assert_held(&ab->base_lock);
+
+ if (!ab->rhead_peer_id || !ab->rhead_peer_addr)
+ return -EPERM;
+
+ ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id,
+ &ab->rhash_peer_id_param, &peer->peer_id);
+ if (ret) {
+ ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n",
+ peer->addr, peer->peer_id, ret);
+ return ret;
+ }
+
+ ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr,
+ &ab->rhash_peer_addr_param, &peer->addr);
+ if (ret) {
+ ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n",
+ peer->addr, peer->peer_id, ret);
+ goto err_clean;
+ }
+
+ return 0;
+
+err_clean:
+ ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,
+ &ab->rhash_peer_id_param);
+ return ret;
+}
+
void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)
{
struct ath11k_peer *peer, *tmp_peer;
@@ -634,6 +681,7 @@
&peer->ast_entry_list, ase_list)
ath11k_peer_del_ast(ar, ast_entry);
+ ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
@@ -743,7 +791,7 @@
struct ath11k_peer *peer;
struct ieee80211_vif *vif = arvif->vif;
struct ath11k_sta *arsta;
- int ret;
+ int ret, err_ret;
lockdep_assert_held(&ar->conf_mutex);
@@ -754,7 +802,7 @@
}
spin_lock_bh(&ar->ab->base_lock);
- peer = ath11k_peer_find_by_pdev_idx(ar->ab, ar->pdev_idx, param->peer_addr);
+ peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr);
if (peer) {
spin_unlock_bh(&ar->ab->base_lock);
return -EINVAL;
@@ -782,22 +830,14 @@
ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",
param->peer_addr, param->vdev_id);
- reinit_completion(&ar->peer_delete_done);
+ ret = -ENOENT;
+ goto cleanup;
+ }
- ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr,
- param->vdev_id);
- if (ret) {
- ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n",
- param->vdev_id, param->peer_addr);
- return ret;
- }
-
- ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id,
- param->peer_addr);
- if (ret)
- return ret;
-
- return -ENOENT;
+ ret = ath11k_peer_rhash_add(ar->ab, peer);
+ if (ret) {
+ spin_unlock_bh(&ar->ab->base_lock);
+ goto cleanup;
}
peer->pdev_idx = ar->pdev_idx;
@@ -831,4 +871,211 @@
spin_unlock_bh(&ar->ab->base_lock);
return 0;
+
+cleanup:
+ reinit_completion(&ar->peer_delete_done);
+
+ err_ret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr,
+ param->vdev_id);
+ if (err_ret) {
+ ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM ret %d\n",
+ param->vdev_id, param->peer_addr, err_ret);
+ goto exit;
+ }
+
+ err_ret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id,
+ param->peer_addr);
+ if (err_ret)
+ ath11k_warn(ar->ab, "failed wait for peer %pM delete done id %d ret %d\n",
+ param->peer_addr, param->vdev_id, err_ret);
+
+exit:
+ return ret;
+}
+
+int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer)
+{
+ int ret;
+
+ lockdep_assert_held(&ab->base_lock);
+
+ if (!ab->rhead_peer_id || !ab->rhead_peer_addr)
+ return -EPERM;
+
+ ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,
+ &ab->rhash_peer_id_param);
+ if (ret) {
+ ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n",
+ peer->addr, peer->peer_id, ret);
+ return ret;
+ }
+
+ ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr,
+ &ab->rhash_peer_addr_param);
+ if (ret) {
+ ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n",
+ peer->addr, peer->peer_id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab)
+{
+ struct rhashtable_params *param;
+ struct rhashtable *rhash_id_tbl;
+ int ret;
+ size_t size;
+
+ if (ab->rhead_peer_id)
+ return 0;
+
+ size = sizeof(*ab->rhead_peer_id);
+ rhash_id_tbl = kzalloc(size, GFP_KERNEL);
+ if (!rhash_id_tbl) {
+ ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n",
+ size);
+ return -ENOMEM;
+ }
+
+ param = &ab->rhash_peer_id_param;
+
+ param->key_offset = offsetof(struct ath11k_peer, peer_id);
+ param->head_offset = offsetof(struct ath11k_peer, rhash_id);
+ param->key_len = sizeof_field(struct ath11k_peer, peer_id);
+ param->automatic_shrinking = true;
+ param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV;
+
+ ret = rhashtable_init(rhash_id_tbl, param);
+ if (ret) {
+ ath11k_warn(ab, "failed to init peer id rhash table %d\n", ret);
+ goto err_free;
+ }
+
+ spin_lock_bh(&ab->base_lock);
+
+ if (!ab->rhead_peer_id) {
+ ab->rhead_peer_id = rhash_id_tbl;
+ } else {
+ spin_unlock_bh(&ab->base_lock);
+ ath11k_warn(ab, "already peer rhash id init done id_tbl: %p\n",
+ ab->rhead_peer_id);
+ goto cleanup_tbl;
+ }
+
+ spin_unlock_bh(&ab->base_lock);
+
+ return 0;
+
+cleanup_tbl:
+ rhashtable_destroy(rhash_id_tbl);
+err_free:
+ kfree(rhash_id_tbl);
+
+ return ret;
+}
+
+static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab)
+{
+ struct rhashtable_params *param;
+ struct rhashtable *rhash_addr_tbl;
+ int ret;
+ size_t size;
+
+ if (ab->rhead_peer_addr)
+ return 0;
+
+ size = sizeof(*ab->rhead_peer_addr);
+ rhash_addr_tbl = kzalloc(size, GFP_KERNEL);
+ if (!rhash_addr_tbl) {
+ ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n",
+ size);
+ return -ENOMEM;
+ }
+
+ param = &ab->rhash_peer_addr_param;
+
+ param->key_offset = offsetof(struct ath11k_peer, addr);
+ param->head_offset = offsetof(struct ath11k_peer, rhash_addr);
+ param->key_len = sizeof_field(struct ath11k_peer, addr);
+ param->automatic_shrinking = true;
+ param->nelem_hint = TARGET_NUM_PEERS_PDEV;
+
+ ret = rhashtable_init(rhash_addr_tbl, param);
+ if (ret) {
+ ath11k_warn(ab, "failed to init peer addr rhash table %d\n", ret);
+ goto err_free;
+ }
+
+ spin_lock_bh(&ab->base_lock);
+
+ if (!ab->rhead_peer_addr) {
+ ab->rhead_peer_addr = rhash_addr_tbl;
+ } else {
+ spin_unlock_bh(&ab->base_lock);
+ ath11k_warn(ab, "already peer rhash addr init done\n");
+ goto cleanup_tbl;
+ }
+
+ spin_unlock_bh(&ab->base_lock);
+
+ return 0;
+
+cleanup_tbl:
+ rhashtable_destroy(rhash_addr_tbl);
+err_free:
+ kfree(rhash_addr_tbl);
+
+ return ret;
+}
+
+static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab)
+{
+ if (!ab->rhead_peer_id)
+ return;
+
+ rhashtable_destroy(ab->rhead_peer_id);
+ kfree(ab->rhead_peer_id);
+ ab->rhead_peer_id = NULL;
+}
+
+static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab)
+{
+ if (!ab->rhead_peer_addr)
+ return;
+
+ rhashtable_destroy(ab->rhead_peer_addr);
+ kfree(ab->rhead_peer_addr);
+ ab->rhead_peer_addr = NULL;
+}
+
+int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab)
+{
+ int ret;
+
+ ret = ath11k_peer_rhash_id_tbl_init(ab);
+ if (ret)
+ return ret;
+
+ ret = ath11k_peer_rhash_addr_tbl_init(ab);
+ if (ret)
+ goto cleanup_tbl;
+
+ return 0;
+
+cleanup_tbl:
+ ath11k_peer_rhash_id_tbl_destroy(ab);
+
+ return ret;
+}
+
+void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab)
+{
+ spin_lock_bh(&ab->base_lock);
+
+ ath11k_peer_rhash_id_tbl_destroy(ab);
+ ath11k_peer_rhash_addr_tbl_destroy(ab);
+
+ spin_unlock_bh(&ab->base_lock);
}
diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h
index e54ced9..aa0835d 100644
--- a/drivers/net/wireless/ath/ath11k/peer.h
+++ b/drivers/net/wireless/ath/ath11k/peer.h
@@ -75,6 +75,10 @@
/* protected by ab->data_lock */
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];
+ /* peer id based rhashtable list pointer */
+ struct rhash_head rhash_id;
+ /* peer addr based rhashtable list pointer */
+ struct rhash_head rhash_addr;
/* Info used in MMIC verification of
* RX fragments
@@ -113,6 +117,9 @@
const u8 *addr);
struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
int vdev_id);
+int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab);
+void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab);
+int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer);
#ifdef CONFIG_ATH11K_NSS_SUPPORT
struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab,
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index c13bc82..cbcb031 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/elf.h>
@@ -4535,9 +4536,8 @@
ath11k_core_qmi_firmware_ready(ab);
set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
- if (country_code) {
- ath11k_reg_update_cc(ab);
- }
+ if (country_code)
+ ath11k_reg_update_cc(ab, country_code);
}
break;
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 281f646..a45182a 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
#include "debug.h"
@@ -16,8 +17,6 @@
#define ETSI_WEATHER_RADAR_BAND_HIGH 5650
#define ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT 600000
-extern char *country_code;
-
static const struct ieee80211_regdomain ath11k_world_regd = {
.n_reg_rules = 3,
.alpha2 = "00",
@@ -128,22 +127,21 @@
"INIT Country code set to fw failed : %d\n", ret);
}
-void ath11k_reg_update_cc(struct ath11k_base *ab)
+void ath11k_reg_update_cc(struct ath11k_base *ab, const char *country_code)
{
struct wmi_init_country_params init_country_param;
struct ath11k_pdev *pdev;
- struct ath11k *ar;
int i, ret;
+ init_country_param.flags = ALPHA_IS_SET;
+ memcpy(&init_country_param.cc_info.alpha2, country_code, 2);
+ init_country_param.cc_info.alpha2[2] = 0;
+
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
- ar = pdev->ar;
- init_country_param.flags = ALPHA_IS_SET;
- memcpy(&init_country_param.cc_info.alpha2, country_code, 2);
- init_country_param.cc_info.alpha2[2] = 0;
- ret = ath11k_wmi_send_init_country_cmd(ar, init_country_param);
+ ret = ath11k_wmi_send_init_country_cmd(pdev->ar, init_country_param);
if (ret)
- ath11k_warn(ar->ab,
+ ath11k_warn(pdev->ar->ab,
"INIT Country code set to fw failed : %d\n", ret);
}
}
@@ -216,6 +214,11 @@
rtnl_lock();
wiphy_lock(ar->hw->wiphy);
+ if (ar->afc.is_6g_afc_power_event_received || ar->afc.switch_to_lpi_indication_received)
+ ar->hw->wiphy->regulatory_flags |= REGULATORY_SET_BY_6GHZ_AFC;
+ else
+ ar->hw->wiphy->regulatory_flags &= ~REGULATORY_SET_BY_6GHZ_AFC;
+
ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy);
wiphy_unlock(ar->hw->wiphy);
rtnl_unlock();
@@ -884,6 +887,69 @@
*tx_power = ath11k_reg_get_afc_eirp_power(ar, bw, cfi);
}
+int ath11k_reg_switch_to_lpi(struct ath11k_base *ab, struct ath11k_afc_info *afc)
+{
+ struct ieee80211_regdomain *regd = NULL;
+ struct ieee80211_regdomain *new_regd = NULL;
+ struct ieee80211_reg_rule *old_rule, *new_regd_rules;
+ struct ath11k *ar = container_of(afc, struct ath11k, afc);
+ int new_reg_rule_cnt = 0, num_regd_rules = 0, num_sp_rules = 0;
+ int i, k, pdev_idx, ret = 0;
+
+ pdev_idx = ar->pdev_idx;
+
+ if (ab->new_regd[pdev_idx]) {
+ regd = ab->new_regd[pdev_idx];
+ } else {
+ regd = ab->default_regd[pdev_idx];
+ }
+
+ if (!regd) {
+ ath11k_warn(ab, "Regulatory domain data not present\n");
+ return -EINVAL;
+ }
+
+ num_regd_rules = regd->n_reg_rules;
+ for (i = 0; i < num_regd_rules; i++) {
+ old_rule = regd->reg_rules + i;
+ if (old_rule->mode == NL80211_REG_AP_SP)
+ num_sp_rules++;
+ }
+
+ new_reg_rule_cnt = num_regd_rules - num_sp_rules;
+
+ new_regd = kzalloc(sizeof(*new_regd) +
+ (sizeof(*new_regd_rules) * new_reg_rule_cnt),
+ GFP_KERNEL);
+ if (!new_regd)
+ return -ENOMEM;
+
+ new_regd->n_reg_rules = new_reg_rule_cnt;
+ memcpy(new_regd->alpha2, regd->alpha2, REG_ALPHA2_LEN + 1);
+ new_regd->dfs_region = ath11k_map_fw_dfs_region(regd->dfs_region);
+ k = 0;
+ for (i = 0; i < num_regd_rules; i++) {
+ old_rule = regd->reg_rules + i;
+
+ if (old_rule->mode == NL80211_REG_AP_SP) {
+ continue;
+ } else {
+ memcpy((new_regd->reg_rules + k), old_rule, sizeof(*new_regd_rules));
+ k++;
+ }
+ }
+
+ ar->afc.switch_to_lpi_indication_received = true;
+
+ spin_lock_bh(&ab->base_lock);
+ kfree(ab->new_regd[pdev_idx]);
+ ab->new_regd[pdev_idx] = new_regd;
+ spin_unlock_bh(&ab->base_lock);
+
+ ieee80211_queue_work(ar->hw, &ar->regd_update_work);
+ return ret;
+}
+
int ath11k_reg_process_afc_power_event(struct ath11k *ar)
{
struct ath11k_base *ab = ar->ab;
@@ -964,6 +1030,9 @@
}
}
+ if (num_new_sp_rules)
+ ar->afc.switch_to_lpi_indication_received = false;
+
/* Remove the old sp rule from regd and add the new intersected sp rules */
new_reg_rule_cnt = num_regd_rules - num_old_sp_rules + num_new_sp_rules;
ath11k_dbg(ab, ATH11K_DBG_AFC,
@@ -972,7 +1041,7 @@
new_regd = kzalloc(sizeof(*new_regd) +
(sizeof(*new_regd_rules) * new_reg_rule_cnt),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!new_regd) {
ret = -ENOMEM;
goto end;
@@ -1060,8 +1129,15 @@
}
status[AFC_AUTH_STATUS_OFFSET] = cpu_to_le32(AFC_AUTH_ERROR);
- memset(mem + (slotid * AFC_SLOT_SIZE), 0, AFC_SLOT_SIZE);
- memcpy(mem + (slotid * AFC_SLOT_SIZE), afc_resp, len);
+
+ if (ab->userpd_id) {
+ memset_io(mem + (slotid * AFC_SLOT_SIZE), 0, AFC_SLOT_SIZE);
+ memcpy_toio(mem + (slotid * AFC_SLOT_SIZE), afc_resp, len);
+ } else {
+ memset(mem + (slotid * AFC_SLOT_SIZE), 0, AFC_SLOT_SIZE);
+ memcpy(mem + (slotid * AFC_SLOT_SIZE), afc_resp, len);
+ }
+
status[AFC_AUTH_STATUS_OFFSET] = cpu_to_le32(AFC_AUTH_SUCCESS);
return 0;
@@ -1073,7 +1149,7 @@
struct ath11k_afc_req_fixed_params *fixed_param = NULL;
int ret = 0;
- fixed_param = kzalloc(sizeof(*fixed_param), GFP_KERNEL);
+ fixed_param = kzalloc(sizeof(*fixed_param), GFP_ATOMIC);
if (!fixed_param)
return -ENOMEM;
@@ -1105,8 +1181,9 @@
}
break;
case REG_AFC_EXPIRY_EVENT_SWITCH_TO_LPI:
- /*TBH*/
- break;
+ ath11k_dbg(ab, ATH11K_DBG_AFC, "AFC switch to LPI indication received\n");
+ ret = ath11k_reg_switch_to_lpi(ab, afc);
+ return ret;
default:
ath11k_dbg(ab, ATH11K_DBG_AFC, "Invalid AFC expiry event subtype %d\n",
afc->event_subtype);
diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h
index a5dfb89..b4939a2 100644
--- a/drivers/net/wireless/ath/ath11k/reg.h
+++ b/drivers/net/wireless/ath/ath11k/reg.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH11K_REG_H
@@ -65,6 +66,6 @@
u16 *center_freq, int pwr_level,
struct cfg80211_chan_def *chan_def,
s8 *tx_power);
-void ath11k_reg_update_cc(struct ath11k_base *ab);
+void ath11k_reg_update_cc(struct ath11k_base *ab, const char *country_code);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c
index bfba684..63edc6e 100644
--- a/drivers/net/wireless/ath/ath11k/testmode.c
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
@@ -61,7 +61,7 @@
return;
}
- cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC, true);
}
/* Returns true if callee consumes the skb and the skb should be discarded.
@@ -141,7 +141,7 @@
goto out;
}
- cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC, false);
out:
spin_unlock_bh(&ar->data_lock);
@@ -265,7 +265,7 @@
goto out;
}
- cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC, false);
out:
spin_unlock_bh(&ar->data_lock);
diff --git a/drivers/net/wireless/ath/ath11k/vendor.c b/drivers/net/wireless/ath/ath11k/vendor.c
index 6ba6a92..b1795b2 100644
--- a/drivers/net/wireless/ath/ath11k/vendor.c
+++ b/drivers/net/wireless/ath/ath11k/vendor.c
@@ -269,7 +269,7 @@
ath11k_dbg(ab, ATH11K_DBG_AFC,
"Sending power update complete for afc request id %llu status code %d\n",
fixed_param.req_id, fixed_param.status_code);
- cfg80211_vendor_event(nl_skb, GFP_KERNEL);
+ cfg80211_vendor_event(nl_skb, GFP_ATOMIC);
return 0;
}
@@ -305,7 +305,7 @@
goto out;
}
- cfg80211_vendor_event(nl_skb, GFP_KERNEL);
+ cfg80211_vendor_event(nl_skb, GFP_ATOMIC);
ath11k_dbg(ar->ab, ATH11K_DBG_AFC,
"Sending expiry event to higher layer of type %d\n",
QCA_WLAN_VENDOR_AFC_EXPIRY_EVENT);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index c6318d8..462acda 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -23,6 +23,8 @@
#define ATH11K_WMI_STA_KICKOUT_TIMEOUT_MS 5000
#define ATH11K_WMI_STA_KICKOUT_LIMIT 100
+#define ATH11K_TEST_CAL_STATUS(val, idx) ((val >> idx) & 1)
+
struct wmi_tlv_policy {
size_t min_len;
};
@@ -154,6 +156,9 @@
= { .min_len = sizeof(struct wmi_afc_event_fixed_param) },
[WMI_TAG_AFC_EXPIRY_EVENT_PARAM]
= { .min_len = sizeof(struct wmi_afc_expiry_event_param) },
+ [WMI_TAG_PDEV_CAL_STATUS_EVENT]
+ = { .min_len = sizeof(struct wmi_pdev_cal_status_event) },
+
};
#define PRIMAP(_hw_mode_) \
@@ -6347,6 +6352,8 @@
struct ieee80211_vif *vif;
struct ath11k_vif *arvif;
struct ath11k_mgmt_frame_stats *mgmt_stats;
+ struct ath11k_peer *peer;
+ struct ath11k_sta *arsta;
u16 frm_type;
spin_lock_bh(&ar->data_lock);
@@ -6386,6 +6393,18 @@
mgmt_stats->tx_compl_fail[frm_type]++;
}
+ if (ieee80211_has_protected(hdr->frame_control) &&
+ ieee80211_is_disassoc(hdr->frame_control)) {
+ spin_lock_bh(&ar->ab->base_lock);
+ peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr1);
+ if (peer && peer->sta) {
+ arsta = (struct ath11k_sta *)peer->sta->drv_priv;
+ complete_all(&arsta->disassoc_comp);
+ arsta->tx_disassoc = false;
+ }
+ spin_unlock_bh(&ar->ab->base_lock);
+ }
+
skip_mgmt_stats:
spin_unlock_bh(&ar->data_lock);
@@ -6457,6 +6476,8 @@
break;
case ATH11K_SCAN_STARTING:
ar->scan.state = ATH11K_SCAN_RUNNING;
+ if (ar->scan.is_roc)
+ ieee80211_ready_on_channel(ar->hw);
complete(&ar->scan.started);
break;
}
@@ -6539,6 +6560,8 @@
case ATH11K_SCAN_RUNNING:
case ATH11K_SCAN_ABORTING:
ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq);
+ if (ar->scan.is_roc && ar->scan.roc_freq == freq)
+ complete(&ar->scan.on_channel);
break;
}
}
@@ -6863,10 +6886,12 @@
return 0;
}
-static void ath11k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src,
+static void ath11k_wmi_pull_pdev_stats_base(struct ath11k *ar,
+ const struct wmi_pdev_stats_base *src,
struct ath11k_fw_stats_pdev *dst)
{
dst->ch_noise_floor = src->chan_nf;
+ ar->chan_noise_floor = src->chan_nf;
dst->tx_frame_count = src->tx_frame_count;
dst->rx_frame_count = src->rx_frame_count;
dst->rx_clear_count = src->rx_clear_count;
@@ -7045,7 +7070,7 @@
if (!dst)
continue;
- ath11k_wmi_pull_pdev_stats_base(&src->base, dst);
+ ath11k_wmi_pull_pdev_stats_base(ar, &src->base, dst);
ath11k_wmi_pull_pdev_stats_tx(&src->tx, dst);
ath11k_wmi_pull_pdev_stats_rx(&src->rx, dst);
list_add_tail(&dst->list, &stats->pdevs);
@@ -8393,7 +8418,6 @@
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_addr(ab, arg.mac_addr);
-
if (!peer) {
spin_unlock_bh(&ab->base_lock);
ath11k_warn(ab, "peer not found %pM\n",
@@ -10063,7 +10087,6 @@
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_addr(ab, peer_addr);
-
if (!peer) {
ath11k_warn(ab, "peer not found %pM\n",
peer_addr);
@@ -10074,11 +10097,9 @@
}
ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id);
-
if (!ar) {
ath11k_warn(ab, "invalid vdev id in peer sta ps state change ev %d",
peer->vdev_id);
-
spin_unlock_bh(&ab->base_lock);
goto exit;
}
@@ -10223,6 +10244,130 @@
}
}
+static int ath11k_wmi_tbtt_offset_subtlv_parser(struct ath11k_base *ab, u16 tag,
+ u16 len, const void *ptr,
+ void *data)
+{
+ int ret = 0;
+ struct ath11k *ar;
+ u64 tx_delay = 0;
+ struct wmi_tbtt_offset_info *tbtt_offset_info;
+ struct ieee80211_chanctx_conf *conf;
+ struct ath11k_vif *arvif;
+
+ tbtt_offset_info = (struct wmi_tbtt_offset_info *)ptr;
+
+ rcu_read_lock();
+ ar = ath11k_mac_get_ar_by_vdev_id(ab, tbtt_offset_info->vdev_id);
+ if (!ar) {
+ ath11k_warn(ab, "ar not found, vdev_id %d\n", tbtt_offset_info->vdev_id);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ arvif = ath11k_mac_get_arvif(ar, tbtt_offset_info->vdev_id);
+ if (!arvif) {
+ ath11k_warn(ab, "arvif not found, vdev_id %d\n",
+ tbtt_offset_info->vdev_id);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (arvif->vdev_type != WMI_VDEV_TYPE_AP) {
+ ret = 0;
+ goto exit;
+ }
+
+ arvif->tbtt_offset = tbtt_offset_info->tbtt_offset;
+
+ conf = rcu_dereference(arvif->vif->chanctx_conf);
+ if (conf && conf->def.chan->band == NL80211_BAND_2GHZ) {
+ /* 1Mbps Beacon: */
+ /* 144 us ( LPREAMBLE) + 48 (PLCP Header)
+ * + 192 (1Mbps, 24 ytes)
+ * = 384 us + 2us(MAC/BB DELAY
+ */
+ tx_delay = 386;
+ } else if (conf && (conf->def.chan->band == NL80211_BAND_5GHZ ||
+ conf->def.chan->band == NL80211_BAND_6GHZ)) {
+ /* 6Mbps Beacon: */
+ /*20(lsig)+2(service)+32(6mbps, 24 bytes)
+ *= 54us + 2us(MAC/BB DELAY)
+ */
+ tx_delay = 56;
+ }
+ arvif->tbtt_offset -= tx_delay;
+
+ ieee80211_queue_work(ar->hw, &arvif->update_bcn_template_work);
+exit:
+ rcu_read_unlock();
+ return ret;
+}
+
+static int ath11k_wmi_tbtt_offset_event_parser(struct ath11k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ int ret = 0;
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi tbtt offset event tag 0x%x of len %d rcvd\n",
+ tag, len);
+
+ switch (tag) {
+ case WMI_TAG_TBTT_OFFSET_EXT_EVENT:
+ break;
+ case WMI_TAG_ARRAY_STRUCT:
+ ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+ ath11k_wmi_tbtt_offset_subtlv_parser,
+ data);
+ break;
+ default:
+ ath11k_warn(ab, "Received invalid tag for wmi tbtt offset event\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int ath11k_wmi_pull_tbtt_offset(struct ath11k_base *ab, struct sk_buff *skb,
+ struct wmi_tbtt_offset_ev_arg *arg)
+{
+ struct wmi_tbtt_offset_event *ev = NULL;
+ struct wmi_tbtt_offset_info tbtt_offset_info = {0};
+ struct wmi_tlv *tlv;
+ int ret;
+ u8 *ptr;
+ u16 tlv_tag;
+
+ ptr = skb->data;
+
+ if (skb->len < (sizeof(*ev) + TLV_HDR_SIZE)) {
+ ath11k_warn(ab, "wmi_tbtt_offset event size invalid\n");
+ return -EINVAL;
+ }
+
+ tlv = (struct wmi_tlv *)ptr;
+ tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header);
+ ptr += sizeof(*tlv);
+
+ if (tlv_tag == WMI_TAG_TBTT_OFFSET_EXT_EVENT) {
+ ev = (struct wmi_tbtt_offset_event *)ptr;
+ } else {
+ ath11k_warn(ab, "tbtt event received with invalid tag\n");
+ return -EINVAL;
+ }
+
+ ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath11k_wmi_tbtt_offset_event_parser,
+ &tbtt_offset_info);
+ if (ret) {
+ ath11k_warn(ab, "failed to parse tbtt tlv %d\n", ret);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int ath11k_wmi_tlv_cfr_capture_evt_parse(struct ath11k_base *ab,
u16 tag, u16 len,
const void *ptr, void *data)
@@ -10267,6 +10412,16 @@
"failed to process cfr cpature ret = %d\n", ret);
}
+void ath11k_wmi_event_tbttoffset_update(struct ath11k_base *ab, struct sk_buff *skb)
+{
+ struct wmi_tbtt_offset_ev_arg arg = {};
+ int ret;
+
+ ret = ath11k_wmi_pull_tbtt_offset(ab, skb, &arg);
+ if (ret)
+ ath11k_warn(ab, "failed to parse tbtt offset event: %d\n", ret);
+}
+
static int ath11k_wmi_peer_ratecode_subtlv_parser(struct ath11k_base *ab,
u16 tag, u16 len,
const void *ptr, void *data)
@@ -10795,6 +10950,87 @@
return ret;
}
+int ath11k_wmi_pdev_get_ani_status(struct ath11k *ar)
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct wmi_pdev_get_ani_status_cmd *cmd = NULL;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_pdev_get_ani_status_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+ WMI_TAG_PDEV_GET_CAL_STATUS_CONFIG_CMD) |
+ FIELD_PREP(WMI_TLV_LEN,
+ sizeof(*cmd) - TLV_HDR_SIZE);
+ cmd->pdev_id = ar->pdev->pdev_id;
+
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_GET_CAL_STATUS_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send get cal status cmd\n");
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi cal status cmd pdev id %d\n",
+ ar->pdev->pdev_id);
+
+ return 0;
+}
+
+static int ath11k_wmi_event_get_ani_status(struct ath11k_base *ab,
+ struct sk_buff *skb)
+{
+ const void **tb;
+ const struct wmi_pdev_cal_status_event *ev;
+ struct ath11k *ar;
+ int ret;
+
+
+ if (!(test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)))
+ return 0;
+
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ret = PTR_ERR(tb);
+ ath11k_warn(ab, "failed to parse tlv of ani status event: %d\n", ret);
+ return ret;
+ }
+
+ ev = tb[WMI_TAG_PDEV_CAL_STATUS_EVENT];
+
+ if (!ev) {
+ ath11k_warn(ab, "failed to fetch ani status ev");
+ kfree(tb);
+ return -EINVAL;
+ }
+
+ if (ATH11K_TEST_CAL_STATUS(ev->cal_valid_bitmap, WMI_CAL_ANI)) {
+ rcu_read_lock();
+ ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id);
+ if (!ar) {
+ ath11k_warn(ab, "ani enabled event in invalid pdev %d\n",
+ ev->pdev_id);
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ spin_lock_bh(&ar->data_lock);
+ ar->ani_enabled = ATH11K_TEST_CAL_STATUS(ev->cal_status_bitmap, WMI_CAL_ANI);
+ spin_unlock_bh(&ar->data_lock);
+ if (!completion_done(&ar->ani_status_event))
+ complete(&ar->ani_status_event);
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "pdev id %d ani status %d\n",
+ ev->pdev_id, ar->ani_enabled);
+ rcu_read_unlock();
+ }
+ return 0;
+}
+
static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
{
struct wmi_cmd_hdr *cmd_hdr;
@@ -10907,8 +11143,10 @@
case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
ath11k_wmi_obss_color_collision_event(ab, skb);
break;
- /* add Unsupported events here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
+ ath11k_wmi_event_tbttoffset_update(ab, skb);
+ break;
+ /* add Unsupported events here */
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
case WMI_TWT_ENABLE_EVENTID:
case WMI_TWT_DISABLE_EVENTID:
@@ -10959,6 +11197,9 @@
case WMI_PDEV_ANI_OFDM_LEVEL_EVENTID:
ath11k_wmi_event_ani_ofdm_level(ab, skb);
break;
+ case WMI_PDEV_GET_CAL_STATUS_EVENTID:
+ ath11k_wmi_event_get_ani_status(ab, skb);
+ break;
case WMI_PEER_RATECODE_LIST_EVENTID:
ath11k_wmi_event_peer_ratecode_list(ab, skb);
break;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 9704dba..a3ade7c 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -301,6 +301,11 @@
WMI_PDEV_SET_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMDID,
WMI_PDEV_SET_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMDID,
WMI_PDEV_GET_TPC_STATS_CMDID,
+ WMI_PDEV_ENABLE_DURATION_BASED_TX_MODE_SELECTION_CMDID,
+ WMI_PDEV_GET_DPD_STATUS_CMDID,
+ WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID,
+ WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID,
+ WMI_PDEV_GET_CAL_STATUS_CMDID,
WMI_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_VDEV),
WMI_VDEV_DELETE_CMDID,
WMI_VDEV_START_REQUEST_CMDID,
@@ -690,6 +695,8 @@
WMI_SERVICE_READY_EXT2_EVENTID,
WMI_PDEV_MULTIPLE_VDEV_RESTART_RESP_EVENTID,
WMI_PDEV_GET_TPC_STATS_EVENTID,
+ WMI_PDEV_GET_DPD_STATUS_EVENTID,
+ WMI_PDEV_GET_CAL_STATUS_EVENTID,
WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV),
WMI_VDEV_STOPPED_EVENTID,
WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
@@ -1973,6 +1980,9 @@
WMI_TAG_AFC_6G_CHANNEL_INFO,
WMI_TAG_AFC_CHAN_EIRP_POWER_INFO,
+ WMI_TAG_PDEV_GET_CAL_STATUS_CONFIG_CMD = 0x3DA,
+ WMI_TAG_PDEV_CAL_STATUS_EVENT,
+
WMI_TAG_MAX
};
@@ -2844,6 +2854,31 @@
u32 start_time;
};
+enum wmi_cal_list {
+ WMI_CAL_ADC,
+ WMI_CAL_BWFILTER,
+ WMI_CAL_PDET_AND_PAL,
+ WMI_CAL_RXDCO,
+ WMI_CAL_COMB_TXLO_TXIQ_RXIQ,
+ WMI_CAL_IBF,
+ WMI_CAL_PA_DROOP,
+ WMI_CAL_DAC,
+ WMI_CAL_ANI,
+ WMI_CAL_NOISE_FLOOR,
+ WMI_CAL_MAX_CAL_LIST
+};
+
+struct wmi_pdev_cal_status_event {
+ u32 pdev_id;
+ u32 cal_valid_bitmap;
+ u32 cal_status_bitmap;
+} __packed;
+
+struct wmi_pdev_get_ani_status_cmd {
+ u32 tlv_header;
+ u32 pdev_id;
+} __packed;
+
struct wmi_pdev_ani_event {
u32 tlv_header;
u32 ani_level;
@@ -4633,6 +4668,24 @@
u32 vdev_id;
} __packed;
+struct wmi_tbtt_offset_info {
+ u32 vdev_id;
+ u32 tbtt_offset;
+ u32 tbtt_qtime_low_us;
+ u32 tbtt_qtime_high_us;
+} __packed;
+
+struct wmi_tbtt_offset_event {
+ u32 num_vdevs;
+} __packed;
+
+struct wmi_tbtt_offset_ev_arg {
+ u32 vdev_id;
+ u32 tbtt_offset;
+ u32 tbtt_qtime_low_us;
+ u32 tbtt_qtime_high_us;
+} __packed;
+
#define WMI_REG_CLIENT_MAX 4
struct wmi_reg_chan_list_cc_ext_event {
@@ -7209,5 +7262,5 @@
u32 vdev_id,
struct ath11k_reg_tpc_power_info *param);
int ath11k_wmi_send_afc_resp_rx_ind(struct ath11k *ar, int data_type);
-
+int ath11k_wmi_pdev_get_ani_status(struct ath11k *ar);
#endif
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c
index f3906db..9ed2559 100644
--- a/drivers/net/wireless/ath/ath6kl/testmode.c
+++ b/drivers/net/wireless/ath/ath6kl/testmode.c
@@ -58,7 +58,7 @@
if (nla_put_u32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD) ||
nla_put(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf))
goto nla_put_failure;
- cfg80211_testmode_event(skb, GFP_KERNEL);
+ cfg80211_testmode_event(skb, GFP_KERNEL, false);
return;
nla_put_failure:
diff --git a/drivers/soc/qcom/bt_ipc.c b/drivers/soc/qcom/bt_ipc.c
index 57927fc..7e2dd76 100644
--- a/drivers/soc/qcom/bt_ipc.c
+++ b/drivers/soc/qcom/bt_ipc.c
@@ -476,14 +476,6 @@
static uint32_t RX_CTXT_OFFSET = 0xe000;
-static bool is_rx_ctxt_valid(struct context_info * rx_ctxt)
-{
- uint8_t ridx = rx_ctxt->sring_buf_info.ridx;
- uint32_t rbuf = rx_ctxt->sring_buf_info.rbuf;
- return (ridx == 0 &&
- rbuf == (RX_CTXT_OFFSET + sizeof(*rx_ctxt)));
-}
-
static void bt_ipc_worker(struct work_struct *work)
{
struct ring_buffer_info *rinfo;
@@ -503,14 +495,6 @@
if (unlikely(!atomic_read(&btDesc->state))) {
btmem->rx_ctxt =
(struct context_info *)(btmem->virt + RX_CTXT_OFFSET);
- if (!is_rx_ctxt_valid(btmem->rx_ctxt)) {
- // rx_ctrx not valid
- // BTSS not ready
- // skip this irq
- spin_unlock(&btDesc->lock);
- enable_irq(ipc->irq);
- return;
- }
}
else
bt_ipc_process_ack(btDesc);
@@ -573,6 +557,9 @@
INIT_WORK(&ipc->work, bt_ipc_worker);
+ irq_set_irqchip_state(ipc->irq, IRQCHIP_STATE_PENDING, 0);
+ irq_set_status_flags(ipc->irq, IRQ_NOAUTOEN);
+
ret = devm_request_threaded_irq(dev, ipc->irq, NULL, bt_ipc_irq_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT, "bt_ipc_irq", btDesc);
diff --git a/drivers/soc/qcom/bt_tty.c b/drivers/soc/qcom/bt_tty.c
index 78c8a4c..6eca6a3 100644
--- a/drivers/soc/qcom/bt_tty.c
+++ b/drivers/soc/qcom/bt_tty.c
@@ -35,6 +35,9 @@
static bool btss_debug;
module_param(btss_debug, bool, 0644);
+static unsigned short btss_boot_delay = 500;
+module_param(btss_boot_delay, ushort, 0644);
+
unsigned int pid_distinct(struct bt_descriptor *btDesc, enum pid_ops action)
{
int ret;
@@ -77,6 +80,11 @@
pid_cursor->pid = pid;
atomic_inc(&pid_cursor->refcnt);
list_add_tail(&pid_cursor->list, pid_q);
+
+ if (!atomic_read(&btDesc->state)) {
+ mdelay(btss_boot_delay);
+ enable_irq(btDesc->ipc.irq);
+ }
out:
return pid_cursor ? atomic_read(&pid_cursor->refcnt) : 0;
}
@@ -191,36 +199,6 @@
}
static
-int bt_rproc_fully_boot(struct bt_descriptor *btDesc, struct rproc *rproc)
-{
- int retries = 3;
- struct device *dev = &btDesc->pdev->dev;
- while (retries) {
- unsigned int wait_ms = 200;
- const unsigned int interval_ms = wait_ms / 10;
- int ret = rproc_boot(rproc);
- if (ret)
- return ret;
- do {
- if (atomic_read(&btDesc->state))
- return ret;
- wait_ms -= interval_ms;
- msleep(interval_ms);
- } while(wait_ms >= interval_ms);
-
- if (!atomic_read(&btDesc->state)) {
- rproc_shutdown(rproc);
- --retries;
- if (retries)
- dev_warn(dev, "bt ipc doesn't start, retry\n");
- }
- else
- return ret;
- }
- return -ETIMEDOUT;
-}
-
-static
int bt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
int ret = 0;
@@ -233,7 +211,7 @@
switch (cmd) {
case IOCTL_IPC_BOOT:
if (arg) {
- ret = bt_rproc_fully_boot(btDesc, rproc);
+ ret = rproc_boot(rproc);
if (ret)
dev_err(dev, "m0 boot fail, ret = %d\n", ret);
else
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index ed46fd7..c7a8c43 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1928,6 +1928,9 @@
if (w_index != 0x5 || (w_value >> 8))
break;
interface = w_value & 0xFF;
+ if (interface >= MAX_CONFIG_INTERFACES ||
+ !os_desc_cfg->interface[interface])
+ break;
buf[6] = w_index;
count = count_ext_prop(os_desc_cfg,
interface);
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 04c142c..ab827c1 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -637,14 +637,17 @@
rndis_set_cmplt_type *resp;
rndis_resp_t *r;
+ BufLength = le32_to_cpu(buf->InformationBufferLength);
+ BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+ if ((BufLength > RNDIS_MAX_TOTAL_SIZE) ||
+ (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE))
+ return -EINVAL;
+
r = rndis_add_response(params, sizeof(rndis_set_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_set_cmplt_type *)r->buf;
- BufLength = le32_to_cpu(buf->InformationBufferLength);
- BufOffset = le32_to_cpu(buf->InformationBufferOffset);
-
#ifdef VERBOSE_DEBUG
pr_debug("%s: Length: %d\n", __func__, BufLength);
pr_debug("%s: Offset: %d\n", __func__, BufOffset);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index cabcbb4..3521d04 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1815,8 +1815,9 @@
spin_lock_irq (&dev->lock);
value = -EINVAL;
if (dev->buf) {
+ spin_unlock_irq(&dev->lock);
kfree(kbuf);
- goto fail;
+ return value;
}
dev->buf = kbuf;
@@ -1863,8 +1864,8 @@
value = usb_gadget_probe_driver(&gadgetfs_driver);
if (value != 0) {
- kfree (dev->buf);
- dev->buf = NULL;
+ spin_lock_irq(&dev->lock);
+ goto fail;
} else {
/* at this point "good" hardware has for the first time
* let the USB the host see us. alternatively, if users
@@ -1881,6 +1882,9 @@
return value;
fail:
+ dev->config = NULL;
+ dev->hs_config = NULL;
+ dev->dev = NULL;
spin_unlock_irq (&dev->lock);
pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
kfree (dev->buf);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index c7044a1..854e68d 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -829,6 +829,11 @@
return hdev->ll_driver == driver;
}
+static inline bool hid_is_usb(struct hid_device *hdev)
+{
+ return hid_is_using_ll_driver(hdev, &usb_hid_driver);
+}
+
#define PM_HINT_FULLON 1<<5
#define PM_HINT_NORMAL 1<<1
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 9535b62..39a1ed5 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -58,6 +58,7 @@
struct sk_buff *skb,
unsigned int cookie);
extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr);
+extern void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid);
extern void br_dev_update_stats(struct net_device *dev,
struct rtnl_link_stats64 *nlstats);
extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2f3f85f..723d002 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1342,6 +1342,11 @@
u8 color;
};
+struct cfg80211_6ghz_power_mode_settings {
+ u8 he_6ghz_pwr_mode;
+ struct cfg80211_beacon_data beacon_next;
+};
+
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
#define CFG80211_MAX_NUM_DIFFERENT_BI 16
@@ -4096,6 +4101,7 @@
* @set_unsol_bcast_probe_resp: Set unsolicited broadcast probe response
* transmission parameters.
* @color_change: initiate a color change (with color change).
+ * @power_mode_change_6ghz: initiate power mode change for 6 GHZ.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -4432,6 +4438,8 @@
int (*color_change)(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_color_change_settings *params);
+ int (*power_mode_change_6ghz)(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_6ghz_power_mode_settings *params);
};
/*
@@ -6828,7 +6836,7 @@
int vendor_event_idx,
int approxlen, gfp_t gfp);
-void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp);
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp, bool testlogs);
/**
* cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply
@@ -6958,7 +6966,7 @@
*/
static inline void cfg80211_vendor_event(struct sk_buff *skb, gfp_t gfp)
{
- __cfg80211_send_event_skb(skb, gfp);
+ __cfg80211_send_event_skb(skb, gfp, false);
}
#ifdef CONFIG_NL80211_TESTMODE
@@ -7059,9 +7067,10 @@
* by cfg80211_testmode_alloc_event_skb(), as an event. It always
* consumes it.
*/
-static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
+static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp,
+ bool testlogs)
{
- __cfg80211_send_event_skb(skb, gfp);
+ __cfg80211_send_event_skb(skb, gfp, testlogs);
}
#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd),
@@ -7825,6 +7834,14 @@
struct cfg80211_chan_def *chandef,
u8 count, bool quiet);
+/*cfg80211_6ghz_power_mode_notify - Notify new HE 6GHZ power mode
+ * @dev: the device on which power mode change is done
+ * @gfp: allocation flags
+ * @cmd: the actual event to be notified
+ */
+void cfg80211_6ghz_power_mode_notify(struct net_device *dev,
+ gfp_t gfp, enum nl80211_commands cmd);
+
/**
* ieee80211_operating_class_to_band - convert operating class to band
*
@@ -8433,4 +8450,14 @@
0, 0);
}
+/*cfg80211_6ghz_power_mode_change_notify -notify 6GHZ power mode change
+ * @dev: the device on which the power mode change is done
+ *
+ * Inform the userspace about the successful change in 6GHZ power mode
+ */
+static inline void cfg80211_6ghz_power_mode_change_notify(struct net_device *dev)
+{
+ cfg80211_6ghz_power_mode_notify(dev, GFP_KERNEL,
+ NL80211_CMD_POWER_MODE_CHANGE_COMPLETED);
+}
#endif /* __NET_CFG80211_H */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 96b25c1..6136b3f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -360,7 +360,8 @@
BSS_CHANGED_HE_OBSS_PD = 1<<28,
BSS_CHANGED_HE_BSS_COLOR = 1<<29,
BSS_CHANGED_FILS_DISCOVERY = 1<<30,
- BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31,
+ BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1ULL<<31,
+ BSS_CHANGED_6G_POWER_MODE = 1ULL<<32,
/* when adding here, make sure to change ieee80211_reconfig */
};
@@ -1829,6 +1830,7 @@
bool color_change_active;
u8 color_change_color;
u8 bmiss_threshold;
+ bool noqueue_enable;
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
@@ -4067,10 +4069,10 @@
void (*bss_info_changed)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
- u32 changed);
+ u64 changed);
void (*nss_bss_info_changed)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- u32 changed);
+ u64 changed);
int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
@@ -5257,6 +5259,16 @@
void ieee80211_csa_finish(struct ieee80211_vif *vif);
/**
+ * ieee80211_6ghz_power_mode_switch_done - notify mac80211 about power mode
+ * change.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After power change for 6 GHZ interface successful, this function must
+ * be called by the driver to notify mac80211 about power mode change.
+ */
+void ieee80211_6ghz_power_mode_switch_done (struct ieee80211_vif *vif);
+
+/**
* ieee80211_beacon_cntdwn_is_complete - find out if countdown reached 1
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
*
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index ddbf9e7..12266ef 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -176,6 +176,7 @@
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
REGULATORY_WIPHY_SELF_MANAGED = BIT(7),
+ REGULATORY_SET_BY_6GHZ_AFC = BIT(8),
};
struct ieee80211_freq_range {
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index e8ccb6f..06f1dfc 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -51,6 +51,7 @@
#define NL80211_MULTICAST_GROUP_VENDOR "vendor"
#define NL80211_MULTICAST_GROUP_NAN "nan"
#define NL80211_MULTICAST_GROUP_TESTMODE "testmode"
+#define NL80211_MULTICAST_GROUP_TESTLOGS "testlogs"
#define NL80211_EDMG_BW_CONFIG_MIN 4
#define NL80211_EDMG_BW_CONFIG_MAX 15
@@ -1213,7 +1214,11 @@
* @NL80211_CMD_AWGN_DETECT: Once AWGN interference is detected on the operating
* channel, userspace is notified with the interference bitmap using
* %NL80211_ATTR_AWGN_INTERFERENCE_BITMAP
-
+ * @NL80211_CMD_SET_6GHZ_POWER_MODE: User can set HE 6 GHZ power mode using userspace
+ * application controlling AP cli(i.e hostapd_cli).This command is used to set this
+ * power mode to the driver.
+ * @NL80211_CMD_POWER_MODE_CHANGE_COMPLETED: Notify the successful 6GHZ power mode change
+ * to userspace to update its power mode
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1459,6 +1464,9 @@
NL80211_CMD_COLOR_CHANGE_ANNOUNCEMENT_COMPLETED,
NL80211_CMD_AWGN_DETECT,
+ NL80211_CMD_SET_6GHZ_POWER_MODE,
+ NL80211_CMD_POWER_MODE_CHANGE_COMPLETED,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -4033,6 +4041,7 @@
NL80211_REGDOM_SET_BY_USER,
NL80211_REGDOM_SET_BY_DRIVER,
NL80211_REGDOM_SET_BY_COUNTRY_IE,
+ NL80211_REGDOM_SET_BY_AFC,
};
/**
diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index 79682c2..4c7f75f 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -914,6 +914,8 @@
opt = fs_parse(fc, &cgroup1_fs_parameters, param, &result);
if (opt == -ENOPARAM) {
if (strcmp(param->key, "source") == 0) {
+ if (param->type != fs_value_is_string)
+ return invalf(fc, "Non-string source");
if (fc->source)
return invalf(fc, "Multiple sources not supported");
fc->source = param->string;
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 639d5e7..332cc2b 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -398,6 +398,7 @@
return 0;
pipe->nrbufs++;
buf->ops = &page_cache_pipe_buf_ops;
+ buf->flags = 0;
get_page(buf->page = page);
buf->offset = offset;
buf->len = bytes;
@@ -523,6 +524,7 @@
if (!page)
break;
pipe->nrbufs++;
+ pipe->bufs[idx].flags = 0;
pipe->bufs[idx].ops = &default_pipe_buf_ops;
pipe->bufs[idx].page = page;
pipe->bufs[idx].offset = 0;
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 353690b..a9d1a05 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -686,6 +686,24 @@
}
EXPORT_SYMBOL_GPL(br_refresh_fdb_entry);
+/* Update timestamp of FDB entries for bridge packets being forwarded by offload engines */
+void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid)
+{
+ struct net_bridge_fdb_entry *fdb;
+ struct net_bridge_port *p = br_port_get_rcu(dev);
+
+ if (!p || p->state == BR_STATE_DISABLED)
+ return;
+
+ rcu_read_lock();
+ fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid);
+ if (likely(fdb)) {
+ fdb->updated = jiffies;
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(br_fdb_entry_refresh);
+
/* Look up the MAC address in the device's bridge fdb table */
struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
const char *addr, __u16 vid)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index c2ee61b..eb90194 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -842,14 +842,16 @@
return;
br = netdev_priv(dev);
- stats = per_cpu_ptr(br->stats, 0);
+ stats = this_cpu_ptr(br->stats);
+ local_bh_disable();
u64_stats_update_begin(&stats->syncp);
stats->rx_packets += nlstats->rx_packets;
stats->rx_bytes += nlstats->rx_bytes;
stats->tx_packets += nlstats->tx_packets;
stats->tx_bytes += nlstats->tx_bytes;
u64_stats_update_end(&stats->syncp);
+ local_bh_enable();
}
EXPORT_SYMBOL_GPL(br_dev_update_stats);
diff --git a/net/core/skbuff_recycle.h b/net/core/skbuff_recycle.h
index ab14aec..c93c87c 100644
--- a/net/core/skbuff_recycle.h
+++ b/net/core/skbuff_recycle.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017, 2022, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -43,7 +43,7 @@
#define SKB_RECYCLE_SIZE 2304
#define SKB_RECYCLE_MIN_SIZE SKB_RECYCLE_SIZE
#define SKB_RECYCLE_MAX_SIZE (3904 - NET_SKB_PAD)
-#define SKB_RECYCLE_MAX_SKBS 1024
+#define SKB_RECYCLE_MAX_SKBS 8192
#define SKB_RECYCLE_SPARE_MAX_SKBS 256
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 480d0b2..8b72b31 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2722,6 +2722,7 @@
rv = 1;
} else if (im) {
if (src_addr) {
+ spin_lock_bh(&im->lock);
for (psf = im->sources; psf; psf = psf->sf_next) {
if (psf->sf_inaddr == src_addr)
break;
@@ -2732,6 +2733,7 @@
im->sfcount[MCAST_EXCLUDE];
else
rv = im->sfcount[MCAST_EXCLUDE] != 0;
+ spin_unlock_bh(&im->lock);
} else
rv = 1; /* unspecified source; tentatively allow */
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 07504da..7611a1a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -927,19 +927,26 @@
struct ieee80211_fils_discovery *fd;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (!params->tmpl || !params->tmpl_len)
- return -EINVAL;
-
fd = &sdata->vif.bss_conf.fils_discovery;
fd->min_interval = params->min_interval;
fd->max_interval = params->max_interval;
old = sdata_dereference(sdata->u.ap.fils_discovery, sdata);
+
+ if (!fd->max_interval) {
+ new = NULL;
+ goto free_old_fils;
+ }
+
+ if (!params->tmpl || !params->tmpl_len)
+ return -EINVAL;
+
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
if (!new)
return -ENOMEM;
new->len = params->tmpl_len;
memcpy(new->data, params->tmpl, params->tmpl_len);
+free_old_fils:
rcu_assign_pointer(sdata->u.ap.fils_discovery, new);
if (old)
@@ -957,17 +964,24 @@
struct unsol_bcast_probe_resp_data *new, *old = NULL;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (!params->tmpl || !params->tmpl_len)
- return -EINVAL;
-
sdata->vif.bss_conf.unsol_bcast_probe_resp_interval = params->interval;
old = sdata_dereference(sdata->u.ap.unsol_bcast_probe_resp, sdata);
+
+ if (!params->interval) {
+ new = NULL;
+ goto free_old_ubpr;
+ }
+
+ if (!params->tmpl || !params->tmpl_len)
+ return -EINVAL;
+
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
if (!new)
return -ENOMEM;
new->len = params->tmpl_len;
memcpy(new->data, params->tmpl, params->tmpl_len);
+free_old_ubpr:
rcu_assign_pointer(sdata->u.ap.unsol_bcast_probe_resp, new);
if (old)
@@ -2923,18 +2937,18 @@
static int ieee80211_get_tx_power(struct wiphy *wiphy,
struct wireless_dev *wdev,
- int *dbm)
+ int *mbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
if (local->ops->get_txpower)
- return drv_get_txpower(local, sdata, dbm);
+ return drv_get_txpower(local, sdata, mbm);
if (!local->use_chanctx)
- *dbm = local->hw.conf.power_level;
+ *mbm = DBM_TO_MBM(local->hw.conf.power_level);
else
- *dbm = sdata->vif.bss_conf.txpower;
+ *mbm = DBM_TO_MBM(sdata->vif.bss_conf.txpower);
return 0;
}
@@ -3451,6 +3465,39 @@
}
}
+void ieee80211_power_mode_switch_work(struct work_struct *work)
+{
+ int ret;
+ u32 changed = 0;
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ power_mode_switch_work);
+ struct ieee80211_local *local = sdata->local;
+
+ sdata_lock(sdata);
+ mutex_lock(&local->mtx);
+
+ if (sdata->vif.csa_active || !sdata->u.ap.next_beacon)
+ goto unlock;
+
+ ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
+ NULL, NULL);
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+
+ if (ret < 0) {
+ goto unlock;
+ }
+ changed = ret;
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+ cfg80211_6ghz_power_mode_change_notify(sdata->dev);
+
+unlock:
+ mutex_unlock(&local->mtx);
+ sdata_unlock(sdata);
+}
+
void ieee80211_csa_finalize_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
@@ -4343,6 +4390,62 @@
return local->ops->set_sar_specs(&local->hw, sar);
}
+void ieee80211_6ghz_power_mode_switch_done(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ ieee80211_queue_work(&sdata->local->hw, &sdata->power_mode_switch_work);
+}
+EXPORT_SYMBOL(ieee80211_6ghz_power_mode_switch_done);
+
+static int ieee80211_set_power_mode_changed_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_6ghz_power_mode_settings *params)
+{
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return -EOPNOTSUPP;
+
+ sdata->u.ap.next_beacon =
+ cfg80211_beacon_dup(¶ms->beacon_next);
+
+ if (!sdata->u.ap.next_beacon)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+ieee80211_6ghz_power_mode_change(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_6ghz_power_mode_settings *params)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+ u64 changed = 0;
+
+ sdata_assert_lock(sdata);
+ mutex_lock(&local->mtx);
+
+ if (sdata->vif.csa_active) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ err = ieee80211_set_power_mode_changed_beacon(sdata, params);
+ if (err)
+ goto out;
+
+ wdev->reg_6g_power_mode = params->he_6ghz_pwr_mode;
+
+ changed = BSS_CHANGED_6G_POWER_MODE;
+ ieee80211_bss_info_change_notify(sdata, changed);
+out:
+ mutex_unlock(&local->mtx);
+
+ return err;
+}
+
static int ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata,
u32 *changed)
{
@@ -4661,4 +4764,5 @@
.set_fils_discovery = ieee80211_set_fils_discovery,
.set_unsol_bcast_probe_resp = ieee80211_set_unsol_bcast_probe_resp,
.color_change = ieee80211_color_change,
+ .power_mode_change_6ghz = ieee80211_6ghz_power_mode_change,
};
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 491661a..01b1851 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -329,6 +329,32 @@
IEEE80211_IF_FILE_RW(bmiss_threshold);
+static ssize_t ieee80211_if_fmt_noqueue_enable(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ return snprintf(buf, buflen, "%u\n", sdata->vif.noqueue_enable);
+}
+
+static ssize_t ieee80211_if_parse_noqueue_enable(struct ieee80211_sub_if_data *sdata,
+ const char *buf, int buflen)
+{
+ int ret;
+ u8 val;
+
+ ret = kstrtou8(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ if (val > 1)
+ return -EINVAL;
+
+ sdata->vif.noqueue_enable = val;
+
+ return buflen;
+}
+
+IEEE80211_IF_FILE_RW(noqueue_enable);
+
static ssize_t
ieee80211_if_parse_wmm_param(struct ieee80211_sub_if_data *sdata,
const char *user_buf, int count)
@@ -839,6 +865,7 @@
DEBUGFS_ADD_MODE(multicast_to_unicast, 0600);
DEBUGFS_ADD_MODE(bmiss_threshold, 0600);
DEBUGFS_ADD_MODE(wmm_param, 0200);
+ DEBUGFS_ADD_MODE(noqueue_enable, 0600);
}
static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
@@ -861,6 +888,7 @@
DEBUGFS_ADD_MODE(estab_plinks, 0400);
DEBUGFS_ADD_MODE(bmiss_threshold, 0600);
DEBUGFS_ADD_MODE(wmm_param, 0200);
+ DEBUGFS_ADD_MODE(noqueue_enable, 0600);
}
static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index c9cc451..1883e6c 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -68,6 +68,9 @@
if (ret == 0)
sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ sdata->vif.noqueue_enable = true;
+
return ret;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 386059a..20f4e92 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -150,7 +150,7 @@
static inline void drv_bss_info_changed(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *info,
- u32 changed)
+ u64 changed)
{
might_sleep();
@@ -181,7 +181,7 @@
static inline void drv_nss_bss_info_changed(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *info,
- u32 changed)
+ u64 changed)
{
might_sleep();
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e4f3dcc..e9e6e16 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -247,6 +247,8 @@
u32 tkip_iv32;
u16 tkip_iv16;
+ u16 last_legacy_rate_idx;
+ enum nl80211_band last_legacy_band;
};
struct ieee80211_csa_settings {
@@ -952,6 +954,7 @@
struct cfg80211_chan_def csa_chandef;
struct work_struct color_change_finalize_work;
+ struct work_struct power_mode_switch_work;
struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
@@ -1658,7 +1661,7 @@
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
- u32 changed);
+ u64 changed);
void ieee80211_nss_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed);
void ieee80211_configure_filter(struct ieee80211_local *local);
@@ -1787,6 +1790,7 @@
/* channel switch handling */
void ieee80211_csa_finalize_work(struct work_struct *work);
+void ieee80211_power_mode_switch_work(struct work_struct *work);
int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params);
/* awgn interference handling */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index ba47dd0..25701fa 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -490,6 +490,7 @@
}
cancel_work_sync(&sdata->color_change_finalize_work);
+ cancel_work_sync(&sdata->power_mode_switch_work);
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
@@ -1623,6 +1624,7 @@
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
INIT_WORK(&sdata->color_change_finalize_work, ieee80211_color_change_finalize_work);
+ INIT_WORK(&sdata->power_mode_switch_work, ieee80211_power_mode_switch_work);
INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index e453a9b..224e801 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -200,7 +200,7 @@
}
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
- u32 changed)
+ u64 changed)
{
struct ieee80211_local *local = sdata->local;
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index fc465bf..cd316b0 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -299,6 +299,7 @@
{
struct ieee80211_tx_info *txinfo = st->info;
int failed = 0;
+ u32 fail_avg;
struct rate_info rinfo;
failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
@@ -316,6 +317,11 @@
sta->mesh->fail_cnt = 0;
}
+ fail_avg = ewma_mesh_fail_avg_read(&sta->mesh->fail_avg);
+ if (!fail_avg)
+ /* init it at a low value - 0 is tricky */
+ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1);
+
/* moving average, scaled to 100.
* feed failure as 100 and success as 0
*/
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index efc6cf6..b71c3ca 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3927,6 +3927,14 @@
status = IEEE80211_SKB_RXCB((rx->skb));
sband = rx->local->hw.wiphy->bands[status->band];
+ if (!status || !sband) {
+ printk(KERN_ERR "status %pM or sband %pM is Null rate_idx %u legacy_band %d\n",
+ status, sband, rx->last_legacy_rate_idx, rx->last_legacy_band);
+ if (status) {
+ printk(KERN_ERR "STATUS idx %d\n", status->rate_idx);
+ printk(KERN_ERR "rate %pM\n", &sband->bitrates[status->rate_idx]);
+ }
+ }
if (status->encoding == RX_ENC_LEGACY)
rate = &sband->bitrates[status->rate_idx];
@@ -4576,6 +4584,16 @@
return;
}
+ /* Get the multicat broadcast stats */
+ ieee80211_mc_bc_stats(((struct ethhdr *)rx->skb->data)->h_dest, rx);
+
+ if (!ieee80211_mc_bc_rx_limit(((struct ethhdr *)rx->skb->data)->h_dest,
+ rx)) {
+ /* free the omitted skb's before returning from here */
+ dev_kfree_skb(skb);
+ return;
+ }
+
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, fast_rx->dev);
memset(skb->cb, 0, sizeof(skb->cb));
@@ -4585,7 +4603,6 @@
netif_receive_skb(skb);
atomic_inc(&sta->rx_netif_pkts);
-
}
static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
@@ -4760,54 +4777,7 @@
/* push the addresses in front */
memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs));
- skb->dev = fast_rx->dev;
-
- ieee80211_rx_stats(fast_rx->dev, skb->len);
-
- /* The seqno index has the same property as needed
- * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
- * for non-QoS-data frames. Here we know it's a data
- * frame, so count MSDUs.
- */
- u64_stats_update_begin(&stats->syncp);
- stats->msdu[rx->seqno_idx]++;
- stats->bytes += orig_len;
- u64_stats_update_end(&stats->syncp);
-
- if (fast_rx->internal_forward) {
- struct sk_buff *xmit_skb = NULL;
- if (is_multicast_ether_addr(addrs.da)) {
- xmit_skb = skb_copy(skb, GFP_ATOMIC);
- } else if (!ether_addr_equal(addrs.da, addrs.sa) &&
- sta_info_get(rx->sdata, addrs.da)) {
- xmit_skb = skb;
- skb = NULL;
- }
-
- if (xmit_skb) {
- /*
- * Send to wireless media and increase priority by 256
- * to keep the received priority instead of
- * reclassifying the frame (see cfg80211_classify8021d).
- */
- xmit_skb->priority += 256;
- xmit_skb->protocol = htons(ETH_P_802_3);
- skb_reset_network_header(xmit_skb);
- skb_reset_mac_header(xmit_skb);
- dev_queue_xmit(xmit_skb);
- }
-
- if (!skb)
- return true;
- }
-
- /* deliver to local stack */
- skb->protocol = eth_type_trans(skb, fast_rx->dev);
- memset(skb->cb, 0, sizeof(skb->cb));
- if (rx->list)
- list_add_tail(&skb->list, rx->list);
- else
- netif_receive_skb(skb);
+ ieee80211_rx_8023(rx, fast_rx, orig_len);
return true;
drop:
@@ -4915,6 +4885,7 @@
struct list_head *list)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
__le16 fc;
@@ -4929,6 +4900,14 @@
rx.local = local;
rx.list = list;
+ if (status && status->encoding == RX_ENC_LEGACY) {
+ rx.last_legacy_rate_idx = status->rate_idx;
+ rx.last_legacy_band = status->band;
+ }
+
+ if (!status)
+ pr_err("STATUS not found\n");
+
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 644a7de..517da04 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2138,7 +2138,7 @@
}
static struct ieee80211_sta_rx_stats *
-sta_get_last_rx_stats(struct sta_info *sta)
+sta_get_last_rx_stats(struct sta_info *sta, bool is_rx_bitrate)
{
struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
struct ieee80211_local *local = sta->local;
@@ -2147,15 +2147,17 @@
if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD))
return stats;
- if (!ieee80211_hw_check(&local->hw, USES_RSS))
+ if (!ieee80211_hw_check(&local->hw, USES_RSS) || !sta->pcpu_rx_stats)
return stats;
for_each_possible_cpu(cpu) {
struct ieee80211_sta_rx_stats *cpustats;
+ u16 rate;
cpustats = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
+ rate = READ_ONCE(cpustats->last_rate);
- if(!cpustats->last_rx)
+ if(!cpustats->last_rx || (is_rx_bitrate && (rate == STA_STATS_RATE_INVALID)))
continue;
if (time_after(cpustats->last_rx, stats->last_rx))
@@ -2219,7 +2221,7 @@
static int sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
{
- u16 rate = READ_ONCE(sta_get_last_rx_stats(sta)->last_rate);
+ u16 rate = READ_ONCE(sta_get_last_rx_stats(sta, true)->last_rate);
if (rate == STA_STATS_RATE_INVALID)
return -EINVAL;
@@ -2319,7 +2321,7 @@
int i, ac, cpu;
struct ieee80211_sta_rx_stats *last_rxstats;
- last_rxstats = sta_get_last_rx_stats(sta);
+ last_rxstats = sta_get_last_rx_stats(sta, false);
sinfo->generation = sdata->local->sta_generation;
@@ -2599,7 +2601,7 @@
unsigned long ieee80211_sta_last_active(struct sta_info *sta)
{
- struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta);
+ struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta, false);
if (!sta->status_stats.last_ack ||
time_after(stats->last_rx, sta->status_stats.last_ack))
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 912019c..a6c0174 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -414,8 +414,12 @@
if (ac_queue == queue ||
(sdata->vif.cab_queue == queue &&
local->queue_stop_reasons[ac_queue] == 0 &&
- skb_queue_empty(&local->pending[ac_queue])))
+ skb_queue_empty(&local->pending[ac_queue]))) {
+ if (sdata->vif.noqueue_enable)
+ continue;
+
netif_wake_subqueue(sdata->dev, ac);
+ }
}
}
}
@@ -514,6 +518,9 @@
for (ac = 0; ac < n_acs; ac++) {
if (sdata->vif.hw_queue[ac] == queue ||
sdata->vif.cab_queue == queue) {
+ if (!local->ops->wake_tx_queue && sdata->vif.noqueue_enable)
+ continue;
+
if (!local->ops->wake_tx_queue) {
netif_stop_subqueue(sdata->dev, ac);
continue;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 57e7703..4a59bf6 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -334,11 +334,17 @@
struct wireless_dev *wdev, *tmp;
ASSERT_RTNL();
- lockdep_assert_wiphy(&rdev->wiphy);
list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
- if (wdev->nl_owner_dead)
+ if (wdev->nl_owner_dead) {
+ if (wdev->netdev)
+ dev_close(wdev->netdev);
+
+ wiphy_lock(&rdev->wiphy);
+ cfg80211_leave(rdev, wdev);
rdev_del_virtual_intf(rdev, wdev);
+ wiphy_unlock(&rdev->wiphy);
+ }
}
}
@@ -350,9 +356,7 @@
destroy_work);
rtnl_lock();
- wiphy_lock(&rdev->wiphy);
cfg80211_destroy_ifaces(rdev);
- wiphy_unlock(&rdev->wiphy);
rtnl_unlock();
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 472549a..dd890dc 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -47,7 +47,8 @@
NL80211_MCGRP_MLME,
NL80211_MCGRP_VENDOR,
NL80211_MCGRP_NAN,
- NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
+ NL80211_MCGRP_TESTMODE,
+ NL80211_MCGRP_TESTLOGS /* keep last - ifdef! */
};
static const struct genl_multicast_group nl80211_mcgrps[] = {
@@ -58,7 +59,8 @@
[NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
[NL80211_MCGRP_NAN] = { .name = NL80211_MULTICAST_GROUP_NAN },
#ifdef CONFIG_NL80211_TESTMODE
- [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
+ [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE },
+ [NL80211_MCGRP_TESTLOGS] = { .name = NL80211_MULTICAST_GROUP_TESTLOGS }
#endif
};
@@ -3021,6 +3023,7 @@
{
struct netlink_ext_ack *extack = info->extack;
struct nlattr **attrs = info->attrs;
+ struct wireless_dev *wdev = info->user_ptr[1];
enum nl80211_regulatory_power_modes mode = NL80211_REG_AP_LPI;
u32 control_freq;
@@ -3033,10 +3036,12 @@
control_freq +=
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]);
- if (info->attrs[NL80211_ATTR_6G_REG_POWER_MODE])
+ if (info->attrs[NL80211_ATTR_6G_REG_POWER_MODE]) {
mode = nla_get_u8(info->attrs[NL80211_ATTR_6G_REG_POWER_MODE]);
- else
- mode = chandef->mode;
+ } else {
+ if (wdev)
+ mode = wdev->reg_6g_power_mode;
+ }
memset(chandef, 0, sizeof(*chandef));
@@ -3632,12 +3637,12 @@
}
if (rdev->ops->get_tx_power) {
- int dbm, ret;
+ int mbm, ret;
- ret = rdev_get_tx_power(rdev, wdev, &dbm);
+ ret = rdev_get_tx_power(rdev, wdev, &mbm);
if (ret == 0 &&
nla_put_u32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
- DBM_TO_MBM(dbm)))
+ mbm))
goto nla_put_failure;
}
@@ -3986,7 +3991,7 @@
return err;
}
-static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
+static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct vif_params params;
@@ -3995,9 +4000,6 @@
int err;
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
- /* to avoid failing a new interface creation due to pending removal */
- cfg80211_destroy_ifaces(rdev);
-
memset(¶ms, 0, sizeof(params));
if (!info->attrs[NL80211_ATTR_IFNAME])
@@ -4085,6 +4087,21 @@
return genlmsg_reply(msg, info);
}
+static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int ret;
+
+ /* to avoid failing a new interface creation due to pending removal */
+ cfg80211_destroy_ifaces(rdev);
+
+ wiphy_lock(&rdev->wiphy);
+ ret = _nl80211_new_interface(skb, info);
+ wiphy_unlock(&rdev->wiphy);
+
+ return ret;
+}
+
static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5245,6 +5262,11 @@
if (ret)
return ret;
+ fd.max_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]);
+ if (!fd.max_interval) {
+ goto set_fils;
+ }
+
if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] ||
!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] ||
!tb[NL80211_FILS_DISCOVERY_ATTR_TMPL])
@@ -5253,8 +5275,8 @@
fd.tmpl_len = nla_len(tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]);
fd.tmpl = nla_data(tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]);
fd.min_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN]);
- fd.max_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]);
+set_fils:
wdev_lock(wdev);
ret = rdev_set_fils_discovery(rdev, dev, &fd);
wdev_unlock(wdev);
@@ -5283,14 +5305,19 @@
if (ret)
return ret;
+ params.interval = nla_get_u32(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT]);
+
+ if (!params.interval)
+ goto set_ubpr;
+
if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] ||
!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL])
return -EINVAL;
params.tmpl = nla_data(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]);
params.tmpl_len = nla_len(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]);
- params.interval = nla_get_u32(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT]);
+set_ubpr:
wdev_lock(wdev);
ret = rdev_set_unsol_bcast_probe_resp(rdev, dev, ¶ms);
wdev_unlock(wdev);
@@ -10698,7 +10725,7 @@
}
EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
-void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp, bool testlogs)
{
struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
void *hdr = ((void **)skb->cb)[1];
@@ -10719,6 +10746,9 @@
if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
mcgrp = NL80211_MCGRP_VENDOR;
+ if (testlogs)
+ mcgrp = NL80211_MCGRP_TESTLOGS;
+
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
skb, 0, mcgrp, gfp);
}
@@ -15001,6 +15031,74 @@
return ret;
}
+static int nl80211_6ghz_power_mode_change(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_6ghz_power_mode_settings params = {};
+ struct net_device *dev = info->user_ptr[1];
+ struct ieee80211_channel *chan;
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_chan_def *chan_def = &wdev->chandef;
+ const struct ieee80211_reg_rule *reg_rule;
+ u32 width;
+ int i, err, num_20mhz_channels, center_freq;
+
+ if (!rdev->ops->power_mode_change_6ghz)
+ return -EOPNOTSUPP;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_6G_REG_POWER_MODE])
+ return -EINVAL;
+
+ params.he_6ghz_pwr_mode = nla_get_u8(info->attrs[NL80211_ATTR_6G_REG_POWER_MODE]);
+
+ if (params.he_6ghz_pwr_mode < NL80211_REG_AP_LPI ||
+ params.he_6ghz_pwr_mode > NL80211_REG_AP_VLP ||
+ params.he_6ghz_pwr_mode == wdev->reg_6g_power_mode) {
+ GENL_SET_ERR_MSG(info, "Invalid 6GHZ power mode configuration\n");
+ return -EINVAL;
+ }
+
+ if (chan_def->width == NL80211_CHAN_WIDTH_20) {
+ width = 20;
+ } else if (chan_def->width == NL80211_CHAN_WIDTH_40) {
+ width = 40;
+ } else if (chan_def->width == NL80211_CHAN_WIDTH_80) {
+ width = 80;
+ } else if (chan_def->width == NL80211_CHAN_WIDTH_160) {
+ width = 160;
+ } else {
+ GENL_SET_ERR_MSG(info, "Unsupported channel width for configuring power mode\n");
+ return -EINVAL;
+ }
+
+ num_20mhz_channels = width / 20 ;
+
+ for (i = 0; i < num_20mhz_channels; i++) {
+ center_freq = chan_def->chan->center_freq + i * 20;
+ chan = ieee80211_get_6g_channel_khz(&rdev->wiphy,
+ MHZ_TO_KHZ(center_freq),
+ params.he_6ghz_pwr_mode);
+
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ GENL_SET_ERR_MSG(info, "Current channel does not support configured power mode\n");
+ return -ENOTSUPP;
+ }
+ }
+
+ err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_next);
+ if (err)
+ return err;
+
+ wdev_lock(wdev);
+ err = rdev_6g_power_mode_change(rdev, dev, ¶ms);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -15088,6 +15186,7 @@
#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
NL80211_FLAG_CHECK_NETDEV_UP)
#define NL80211_FLAG_CLEAR_SKB 0x20
+#define NL80211_FLAG_NO_WIPHY_MTX 0x40
static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info)
@@ -15139,7 +15238,7 @@
info->user_ptr[0] = rdev;
}
- if (rdev) {
+ if (rdev && !(ops->internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) {
wiphy_lock(&rdev->wiphy);
/* we keep the mutex locked until post_doit */
__release(&rdev->wiphy.mtx);
@@ -15164,7 +15263,8 @@
}
}
- if (info->user_ptr[0]) {
+ if (info->user_ptr[0] &&
+ !(ops->internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) {
struct cfg80211_registered_device *rdev = info->user_ptr[0];
/* we kept the mutex locked since pre_doit */
@@ -15330,7 +15430,9 @@
.doit = nl80211_new_interface,
.flags = GENL_UNS_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
- NL80211_FLAG_NEED_RTNL,
+ NL80211_FLAG_NEED_RTNL |
+ /* we take the wiphy mutex later ourselves */
+ NL80211_FLAG_NO_WIPHY_MTX,
},
{
.cmd = NL80211_CMD_DEL_INTERFACE,
@@ -15625,7 +15727,9 @@
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_wiphy_netns,
.flags = GENL_UNS_ADMIN_PERM,
- .internal_flags = NL80211_FLAG_NEED_WIPHY,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL |
+ NL80211_FLAG_NO_WIPHY_MTX,
},
{
.cmd = NL80211_CMD_GET_SURVEY,
@@ -16090,6 +16194,14 @@
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_SET_6GHZ_POWER_MODE,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = nl80211_6ghz_power_mode_change,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_family nl80211_fam __ro_after_init = {
@@ -17775,6 +17887,46 @@
}
EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
+void cfg80211_6ghz_power_mode_notify(struct net_device *dev,
+ gfp_t gfp, enum nl80211_commands cmd)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ u8 he_6ghz_power_mode = wdev->reg_6g_power_mode;
+
+ ASSERT_WDEV_LOCK(wdev);
+ trace_cfg80211_6ghz_power_mode_notify(dev, cmd, he_6ghz_power_mode);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
+ goto nla_put_failure;
+
+ if(nla_put_u32(msg, NL80211_ATTR_6G_REG_POWER_MODE, he_6ghz_power_mode))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+ return;
+
+nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_6ghz_power_mode_notify);
+
void cfg80211_bss_color_notify(struct net_device *dev,
gfp_t gfp, enum nl80211_commands cmd,
u8 count, u64 color_bitmap)
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 5f5b9c3..efc83d4 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -573,12 +573,12 @@
}
static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, int *dbm)
+ struct wireless_dev *wdev, int *mbm)
{
int ret;
trace_rdev_get_tx_power(&rdev->wiphy, wdev);
- ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
- trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
+ ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, mbm);
+ trace_rdev_return_int_int(&rdev->wiphy, ret, MBM_TO_DBM(*mbm));
return ret;
}
@@ -1382,6 +1382,19 @@
return ret;
}
+static inline int rdev_6g_power_mode_change(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_6ghz_power_mode_settings *params)
+{
+ int ret;
+
+ trace_rdev_power_mode_change(&rdev->wiphy, dev, params);
+ ret = rdev->ops->power_mode_change_6ghz(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+
+ return ret;
+}
+
static inline int rdev_color_change(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_color_change_settings *params)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6d70f11..9b4763f 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -3159,7 +3159,10 @@
request.wiphy_idx = get_wiphy_idx(wiphy);
request.alpha2[0] = regd->alpha2[0];
request.alpha2[1] = regd->alpha2[1];
- request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+ if (wiphy->regulatory_flags & REGULATORY_SET_BY_6GHZ_AFC)
+ request.initiator = NL80211_REGDOM_SET_BY_AFC;
+ else
+ request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
nl80211_send_wiphy_reg_change_event(&request);
}
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 9d34885..3a70072 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -3612,6 +3612,26 @@
__entry->interval, __entry->tmpl_len)
);
+TRACE_EVENT(rdev_power_mode_change,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_6ghz_power_mode_settings *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u8, pwr_mode)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->pwr_mode = params->he_6ghz_pwr_mode;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
+ ", pwr_mode: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->pwr_mode)
+);
+
TRACE_EVENT(rdev_color_change,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_color_change_settings *params),
@@ -3636,6 +3656,25 @@
__entry->count)
);
+TRACE_EVENT(cfg80211_6ghz_power_mode_notify,
+ TP_PROTO(struct net_device *netdev,
+ enum nl80211_commands cmd,
+ u8 he_6ghz_power_mode),
+ TP_ARGS(netdev, cmd,he_6ghz_power_mode),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(enum nl80211_commands,cmd)
+ __field(u8,he_6ghz_power_mode)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->cmd = cmd;
+ __entry->he_6ghz_power_mode = he_6ghz_power_mode;
+ ),
+ TP_printk(NETDEV_PR_FMT ", cmd: %x, he_6ghz_power_mode: %u",
+ NETDEV_PR_ARG, __entry->cmd, __entry->he_6ghz_power_mode)
+);
+
TRACE_EVENT(cfg80211_bss_color_notify,
TP_PROTO(struct net_device *netdev,
enum nl80211_commands cmd,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index a55f41a..5d8cb26 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1379,7 +1379,7 @@
25599, /* 4.166666... */
17067, /* 2.777777... */
12801, /* 2.083333... */
- 11769, /* 1.851851... */
+ 11377, /* 1.851725... */
10239, /* 1.666666... */
8532, /* 1.388888... */
7680, /* 1.250000... */
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index a8320dc..baa7511 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -964,7 +964,7 @@
/* well... oh well */
data->txpower.fixed = 1;
data->txpower.disabled = rfkill_blocked(rdev->rfkill);
- data->txpower.value = val;
+ data->txpower.value = MBM_TO_DBM(val);
data->txpower.flags = IW_TXPOW_DBM;
return 0;