diff --git a/MAINTAINERS b/MAINTAINERS index fd86604f3a9c..5e3709ebc234 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -152,8 +152,8 @@ F: drivers/scsi/53c700* 6LOWPAN GENERIC (BTLE/IEEE 802.15.4) M: Alexander Aring -L: linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers) L: linux-bluetooth@vger.kernel.org +L: linux-wpan@vger.kernel.org S: Maintained F: net/6lowpan/ F: include/net/6lowpan.h @@ -4597,13 +4597,14 @@ F: drivers/idle/i7300_idle.c IEEE 802.15.4 SUBSYSTEM M: Alexander Aring -L: linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers) -W: http://apps.sourceforge.net/trac/linux-zigbee -T: git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git +L: linux-wpan@vger.kernel.org +W: https://github.com/linux-wpan +T: git git://github.com/linux-wpan/linux-wpan-next.git S: Maintained F: net/ieee802154/ F: net/mac802154/ F: drivers/net/ieee802154/ +F: Documentation/networking/ieee802154.txt IGUANAWORKS USB IR TRANSCEIVER M: Sean Young @@ -6373,7 +6374,7 @@ M: Lauro Ramos Venancio M: Aloisio Almeida Jr M: Samuel Ortiz L: linux-wireless@vger.kernel.org -L: linux-nfc@lists.01.org (moderated for non-subscribers) +L: linux-nfc@lists.01.org (subscribers-only) S: Supported F: net/nfc/ F: include/net/nfc/ diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 11115bbe115c..004d6aa671ce 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -21,6 +21,14 @@ #include #include +enum bcma_boot_dev { + BCMA_BOOT_DEV_UNK = 0, + BCMA_BOOT_DEV_ROM, + BCMA_BOOT_DEV_PARALLEL, + BCMA_BOOT_DEV_SERIAL, + BCMA_BOOT_DEV_NAND, +}; + static const char * const part_probes[] = { "bcm47xxpart", NULL }; static struct physmap_flash_data bcma_pflash_data = { @@ -229,11 +237,51 @@ u32 bcma_cpu_clock(struct bcma_drv_mips *mcore) } EXPORT_SYMBOL(bcma_cpu_clock); +static enum bcma_boot_dev bcma_boot_dev(struct bcma_bus *bus) +{ + struct bcma_drv_cc *cc = &bus->drv_cc; + u8 cc_rev = cc->core->id.rev; + + if (cc_rev == 42) { + struct bcma_device *core; + + core = bcma_find_core(bus, BCMA_CORE_NS_ROM); + if (core) { + switch (bcma_aread32(core, BCMA_IOST) & + BCMA_NS_ROM_IOST_BOOT_DEV_MASK) { + case BCMA_NS_ROM_IOST_BOOT_DEV_NOR: + return BCMA_BOOT_DEV_SERIAL; + case BCMA_NS_ROM_IOST_BOOT_DEV_NAND: + return BCMA_BOOT_DEV_NAND; + case BCMA_NS_ROM_IOST_BOOT_DEV_ROM: + default: + return BCMA_BOOT_DEV_ROM; + } + } + } else { + if (cc_rev == 38) { + if (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT) + return BCMA_BOOT_DEV_NAND; + else if (cc->status & BIT(5)) + return BCMA_BOOT_DEV_ROM; + } + + if ((cc->capabilities & BCMA_CC_CAP_FLASHT) == + BCMA_CC_FLASHT_PARA) + return BCMA_BOOT_DEV_PARALLEL; + else + return BCMA_BOOT_DEV_SERIAL; + } + + return BCMA_BOOT_DEV_SERIAL; +} + static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) { struct bcma_bus *bus = mcore->core->bus; struct bcma_drv_cc *cc = &bus->drv_cc; struct bcma_pflash *pflash = &cc->pflash; + enum bcma_boot_dev boot_dev; switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: @@ -269,6 +317,20 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) bcma_nflash_init(cc); } } + + /* Determine flash type this SoC boots from */ + boot_dev = bcma_boot_dev(bus); + switch (boot_dev) { + case BCMA_BOOT_DEV_PARALLEL: + case BCMA_BOOT_DEV_SERIAL: + /* TODO: Init NVRAM using BCMA_SOC_FLASH2 window */ + break; + case BCMA_BOOT_DEV_NAND: + /* TODO: Init NVRAM using BCMA_SOC_FLASH1 window */ + break; + default: + break; + } } void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c index 3475e600011a..1edd7e064621 100644 --- a/drivers/bcma/host_soc.c +++ b/drivers/bcma/host_soc.c @@ -134,12 +134,16 @@ static void bcma_host_soc_block_write(struct bcma_device *core, static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset) { + if (WARN_ONCE(!core->io_wrap, "Accessed core has no wrapper/agent\n")) + return ~0; return readl(core->io_wrap + offset); } static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset, u32 value) { + if (WARN_ONCE(!core->io_wrap, "Accessed core has no wrapper/agent\n")) + return; writel(value, core->io_wrap + offset); } diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index b4764c6bcf17..e9bd77249a4c 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -421,10 +421,13 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr, core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE); if (!core->io_addr) return -ENOMEM; - core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE); - if (!core->io_wrap) { - iounmap(core->io_addr); - return -ENOMEM; + if (core->wrap) { + core->io_wrap = ioremap_nocache(core->wrap, + BCMA_CORE_SIZE); + if (!core->io_wrap) { + iounmap(core->io_addr); + return -ENOMEM; + } } } return 0; diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index a0d7355ef127..d85ced27ebd5 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -88,6 +88,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x04CA, 0x300b) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0930, 0x0220) }, + { USB_DEVICE(0x0930, 0x0227) }, { USB_DEVICE(0x0b05, 0x17d0) }, { USB_DEVICE(0x0CF3, 0x0036) }, { USB_DEVICE(0x0CF3, 0x3004) }, @@ -138,6 +139,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index dfa5043e68ba..35e63aaa6f80 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -61,7 +61,7 @@ MODULE_LICENSE("GPL"); /* ======================== Local structures ======================== */ -typedef struct bluecard_info_t { +struct bluecard_info { struct pcmcia_device *p_dev; struct hci_dev *hdev; @@ -78,7 +78,7 @@ typedef struct bluecard_info_t { unsigned char ctrl_reg; unsigned long hw_state; /* Status of the hardware and LED control */ -} bluecard_info_t; +}; static int bluecard_config(struct pcmcia_device *link); @@ -157,7 +157,7 @@ static void bluecard_detach(struct pcmcia_device *p_dev); static void bluecard_activity_led_timeout(u_long arg) { - bluecard_info_t *info = (bluecard_info_t *)arg; + struct bluecard_info *info = (struct bluecard_info *)arg; unsigned int iobase = info->p_dev->resource[0]->start; if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) @@ -173,7 +173,7 @@ static void bluecard_activity_led_timeout(u_long arg) } -static void bluecard_enable_activity_led(bluecard_info_t *info) +static void bluecard_enable_activity_led(struct bluecard_info *info) { unsigned int iobase = info->p_dev->resource[0]->start; @@ -215,7 +215,7 @@ static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, i } -static void bluecard_write_wakeup(bluecard_info_t *info) +static void bluecard_write_wakeup(struct bluecard_info *info) { if (!info) { BT_ERR("Unknown device"); @@ -368,7 +368,8 @@ static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, in } -static void bluecard_receive(bluecard_info_t *info, unsigned int offset) +static void bluecard_receive(struct bluecard_info *info, + unsigned int offset) { unsigned int iobase; unsigned char buf[31]; @@ -497,7 +498,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) { - bluecard_info_t *info = dev_inst; + struct bluecard_info *info = dev_inst; unsigned int iobase; unsigned char reg; @@ -562,7 +563,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) { - bluecard_info_t *info = hci_get_drvdata(hdev); + struct bluecard_info *info = hci_get_drvdata(hdev); struct sk_buff *skb; /* Ericsson baud rate command */ @@ -611,7 +612,7 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) static int bluecard_hci_flush(struct hci_dev *hdev) { - bluecard_info_t *info = hci_get_drvdata(hdev); + struct bluecard_info *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -622,7 +623,7 @@ static int bluecard_hci_flush(struct hci_dev *hdev) static int bluecard_hci_open(struct hci_dev *hdev) { - bluecard_info_t *info = hci_get_drvdata(hdev); + struct bluecard_info *info = hci_get_drvdata(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); @@ -643,7 +644,7 @@ static int bluecard_hci_open(struct hci_dev *hdev) static int bluecard_hci_close(struct hci_dev *hdev) { - bluecard_info_t *info = hci_get_drvdata(hdev); + struct bluecard_info *info = hci_get_drvdata(hdev); if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; @@ -663,7 +664,7 @@ static int bluecard_hci_close(struct hci_dev *hdev) static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - bluecard_info_t *info = hci_get_drvdata(hdev); + struct bluecard_info *info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -691,7 +692,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* ======================== Card services HCI interaction ======================== */ -static int bluecard_open(bluecard_info_t *info) +static int bluecard_open(struct bluecard_info *info) { unsigned int iobase = info->p_dev->resource[0]->start; struct hci_dev *hdev; @@ -806,7 +807,7 @@ static int bluecard_open(bluecard_info_t *info) } -static int bluecard_close(bluecard_info_t *info) +static int bluecard_close(struct bluecard_info *info) { unsigned int iobase = info->p_dev->resource[0]->start; struct hci_dev *hdev = info->hdev; @@ -833,7 +834,7 @@ static int bluecard_close(bluecard_info_t *info) static int bluecard_probe(struct pcmcia_device *link) { - bluecard_info_t *info; + struct bluecard_info *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); @@ -857,7 +858,7 @@ static void bluecard_detach(struct pcmcia_device *link) static int bluecard_config(struct pcmcia_device *link) { - bluecard_info_t *info = link->priv; + struct bluecard_info *info = link->priv; int i, n; link->config_index = 0x20; @@ -897,7 +898,7 @@ static int bluecard_config(struct pcmcia_device *link) static void bluecard_release(struct pcmcia_device *link) { - bluecard_info_t *info = link->priv; + struct bluecard_info *info = link->priv; bluecard_close(info); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 1d82721cf9c6..4f7e8d400bc0 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -67,7 +67,7 @@ MODULE_FIRMWARE("BT3CPCC.bin"); /* ======================== Local structures ======================== */ -typedef struct bt3c_info_t { +struct bt3c_info { struct pcmcia_device *p_dev; struct hci_dev *hdev; @@ -80,7 +80,7 @@ typedef struct bt3c_info_t { unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; -} bt3c_info_t; +}; static int bt3c_config(struct pcmcia_device *link); @@ -175,7 +175,7 @@ static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) } -static void bt3c_write_wakeup(bt3c_info_t *info) +static void bt3c_write_wakeup(struct bt3c_info *info) { if (!info) { BT_ERR("Unknown device"); @@ -214,7 +214,7 @@ static void bt3c_write_wakeup(bt3c_info_t *info) } -static void bt3c_receive(bt3c_info_t *info) +static void bt3c_receive(struct bt3c_info *info) { unsigned int iobase; int size = 0, avail; @@ -336,7 +336,7 @@ static void bt3c_receive(bt3c_info_t *info) static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) { - bt3c_info_t *info = dev_inst; + struct bt3c_info *info = dev_inst; unsigned int iobase; int iir; irqreturn_t r = IRQ_NONE; @@ -388,7 +388,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) static int bt3c_hci_flush(struct hci_dev *hdev) { - bt3c_info_t *info = hci_get_drvdata(hdev); + struct bt3c_info *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -418,7 +418,7 @@ static int bt3c_hci_close(struct hci_dev *hdev) static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - bt3c_info_t *info = hci_get_drvdata(hdev); + struct bt3c_info *info = hci_get_drvdata(hdev); unsigned long flags; switch (bt_cb(skb)->pkt_type) { @@ -451,7 +451,8 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* ======================== Card services HCI interaction ======================== */ -static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware, +static int bt3c_load_firmware(struct bt3c_info *info, + const unsigned char *firmware, int count) { char *ptr = (char *) firmware; @@ -536,7 +537,7 @@ static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware, } -static int bt3c_open(bt3c_info_t *info) +static int bt3c_open(struct bt3c_info *info) { const struct firmware *firmware; struct hci_dev *hdev; @@ -603,7 +604,7 @@ static int bt3c_open(bt3c_info_t *info) } -static int bt3c_close(bt3c_info_t *info) +static int bt3c_close(struct bt3c_info *info) { struct hci_dev *hdev = info->hdev; @@ -620,7 +621,7 @@ static int bt3c_close(bt3c_info_t *info) static int bt3c_probe(struct pcmcia_device *link) { - bt3c_info_t *info; + struct bt3c_info *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); @@ -683,7 +684,7 @@ static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev, static int bt3c_config(struct pcmcia_device *link) { - bt3c_info_t *info = link->priv; + struct bt3c_info *info = link->priv; int i; unsigned long try; @@ -724,7 +725,7 @@ static int bt3c_config(struct pcmcia_device *link) static void bt3c_release(struct pcmcia_device *link) { - bt3c_info_t *info = link->priv; + struct bt3c_info *info = link->priv; bt3c_close(info); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index fb948f02eda5..abb4d2106db4 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -62,7 +62,7 @@ MODULE_LICENSE("GPL"); /* ======================== Local structures ======================== */ -typedef struct btuart_info_t { +struct btuart_info { struct pcmcia_device *p_dev; struct hci_dev *hdev; @@ -75,7 +75,7 @@ typedef struct btuart_info_t { unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; -} btuart_info_t; +}; static int btuart_config(struct pcmcia_device *link); @@ -127,7 +127,7 @@ static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) } -static void btuart_write_wakeup(btuart_info_t *info) +static void btuart_write_wakeup(struct btuart_info *info) { if (!info) { BT_ERR("Unknown device"); @@ -172,7 +172,7 @@ static void btuart_write_wakeup(btuart_info_t *info) } -static void btuart_receive(btuart_info_t *info) +static void btuart_receive(struct btuart_info *info) { unsigned int iobase; int boguscount = 0; @@ -286,7 +286,7 @@ static void btuart_receive(btuart_info_t *info) static irqreturn_t btuart_interrupt(int irq, void *dev_inst) { - btuart_info_t *info = dev_inst; + struct btuart_info *info = dev_inst; unsigned int iobase; int boguscount = 0; int iir, lsr; @@ -340,7 +340,8 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst) } -static void btuart_change_speed(btuart_info_t *info, unsigned int speed) +static void btuart_change_speed(struct btuart_info *info, + unsigned int speed) { unsigned long flags; unsigned int iobase; @@ -397,7 +398,7 @@ static void btuart_change_speed(btuart_info_t *info, unsigned int speed) static int btuart_hci_flush(struct hci_dev *hdev) { - btuart_info_t *info = hci_get_drvdata(hdev); + struct btuart_info *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -427,7 +428,7 @@ static int btuart_hci_close(struct hci_dev *hdev) static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - btuart_info_t *info = hci_get_drvdata(hdev); + struct btuart_info *info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -455,7 +456,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* ======================== Card services HCI interaction ======================== */ -static int btuart_open(btuart_info_t *info) +static int btuart_open(struct btuart_info *info) { unsigned long flags; unsigned int iobase = info->p_dev->resource[0]->start; @@ -521,7 +522,7 @@ static int btuart_open(btuart_info_t *info) } -static int btuart_close(btuart_info_t *info) +static int btuart_close(struct btuart_info *info) { unsigned long flags; unsigned int iobase = info->p_dev->resource[0]->start; @@ -550,7 +551,7 @@ static int btuart_close(btuart_info_t *info) static int btuart_probe(struct pcmcia_device *link) { - btuart_info_t *info; + struct btuart_info *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); @@ -613,7 +614,7 @@ static int btuart_check_config_notpicky(struct pcmcia_device *p_dev, static int btuart_config(struct pcmcia_device *link) { - btuart_info_t *info = link->priv; + struct btuart_info *info = link->priv; int i; int try; @@ -654,7 +655,7 @@ static int btuart_config(struct pcmcia_device *link) static void btuart_release(struct pcmcia_device *link) { - btuart_info_t *info = link->priv; + struct btuart_info *info = link->priv; btuart_close(info); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 292c38e8aa17..0527b29c3954 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -165,6 +165,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 2bd8fad17206..78e10f0c65b2 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -62,7 +62,7 @@ MODULE_LICENSE("GPL"); /* ======================== Local structures ======================== */ -typedef struct dtl1_info_t { +struct dtl1_info { struct pcmcia_device *p_dev; struct hci_dev *hdev; @@ -78,7 +78,7 @@ typedef struct dtl1_info_t { unsigned long rx_state; unsigned long rx_count; struct sk_buff *rx_skb; -} dtl1_info_t; +}; static int dtl1_config(struct pcmcia_device *link); @@ -94,11 +94,11 @@ static int dtl1_config(struct pcmcia_device *link); #define RECV_WAIT_DATA 1 -typedef struct { +struct nsh { u8 type; u8 zero; u16 len; -} __packed nsh_t; /* Nokia Specific Header */ +} __packed; /* Nokia Specific Header */ #define NSHL 4 /* Nokia Specific Header Length */ @@ -126,7 +126,7 @@ static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) } -static void dtl1_write_wakeup(dtl1_info_t *info) +static void dtl1_write_wakeup(struct dtl1_info *info) { if (!info) { BT_ERR("Unknown device"); @@ -176,7 +176,7 @@ static void dtl1_write_wakeup(dtl1_info_t *info) } -static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb) +static void dtl1_control(struct dtl1_info *info, struct sk_buff *skb) { u8 flowmask = *(u8 *)skb->data; int i; @@ -199,10 +199,10 @@ static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb) } -static void dtl1_receive(dtl1_info_t *info) +static void dtl1_receive(struct dtl1_info *info) { unsigned int iobase; - nsh_t *nsh; + struct nsh *nsh; int boguscount = 0; if (!info) { @@ -227,7 +227,7 @@ static void dtl1_receive(dtl1_info_t *info) } *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); - nsh = (nsh_t *)info->rx_skb->data; + nsh = (struct nsh *)info->rx_skb->data; info->rx_count--; @@ -287,7 +287,7 @@ static void dtl1_receive(dtl1_info_t *info) static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) { - dtl1_info_t *info = dev_inst; + struct dtl1_info *info = dev_inst; unsigned int iobase; unsigned char msr; int boguscount = 0; @@ -365,7 +365,7 @@ static int dtl1_hci_open(struct hci_dev *hdev) static int dtl1_hci_flush(struct hci_dev *hdev) { - dtl1_info_t *info = hci_get_drvdata(hdev); + struct dtl1_info *info = hci_get_drvdata(hdev); /* Drop TX queue */ skb_queue_purge(&(info->txq)); @@ -387,9 +387,9 @@ static int dtl1_hci_close(struct hci_dev *hdev) static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - dtl1_info_t *info = hci_get_drvdata(hdev); + struct dtl1_info *info = hci_get_drvdata(hdev); struct sk_buff *s; - nsh_t nsh; + struct nsh nsh; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -436,7 +436,7 @@ static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* ======================== Card services HCI interaction ======================== */ -static int dtl1_open(dtl1_info_t *info) +static int dtl1_open(struct dtl1_info *info) { unsigned long flags; unsigned int iobase = info->p_dev->resource[0]->start; @@ -505,7 +505,7 @@ static int dtl1_open(dtl1_info_t *info) } -static int dtl1_close(dtl1_info_t *info) +static int dtl1_close(struct dtl1_info *info) { unsigned long flags; unsigned int iobase = info->p_dev->resource[0]->start; @@ -534,7 +534,7 @@ static int dtl1_close(dtl1_info_t *info) static int dtl1_probe(struct pcmcia_device *link) { - dtl1_info_t *info; + struct dtl1_info *info; /* Create new info device */ info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL); @@ -552,7 +552,7 @@ static int dtl1_probe(struct pcmcia_device *link) static void dtl1_detach(struct pcmcia_device *link) { - dtl1_info_t *info = link->priv; + struct dtl1_info *info = link->priv; dtl1_close(info); pcmcia_disable_device(link); @@ -571,7 +571,7 @@ static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data) static int dtl1_config(struct pcmcia_device *link) { - dtl1_info_t *info = link->priv; + struct dtl1_info *info = link->priv; int ret; /* Look for a generic full-sized window */ diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index caacb422995d..a22838669b4e 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -237,7 +237,7 @@ static void h5_pkt_cull(struct h5 *h5) break; to_remove--; - seq = (seq - 1) % 8; + seq = (seq - 1) & 0x07; } if (seq != h5->rx_ack) diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index fd9e5305e77f..c1a4ade32772 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -261,6 +261,7 @@ enum ATH_DEBUG { ATH_DBG_MCI = 0x00008000, ATH_DBG_DFS = 0x00010000, ATH_DBG_WOW = 0x00020000, + ATH_DBG_CHAN_CTX = 0x00040000, ATH_DBG_ANY = 0xffffffff }; diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index a6f5285235af..1053bb5f2cdc 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -25,6 +25,7 @@ config ATH10K_DEBUG config ATH10K_DEBUGFS bool "Atheros ath10k debugfs support" depends on ATH10K + select RELAY ---help--- Enabled debugfs support diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index a4179f49ee1f..2cfb63ca9327 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -10,6 +10,7 @@ ath10k_core-y += mac.o \ wmi.o \ bmi.o +ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 17d221abd58c..3d29b0875b3e 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -22,7 +22,7 @@ void ath10k_bmi_start(struct ath10k *ar) { - ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n"); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n"); ar->bmi.done_sent = false; } @@ -33,10 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar) u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n"); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n"); if (ar->bmi.done_sent) { - ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n"); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n"); return 0; } @@ -45,7 +45,7 @@ int ath10k_bmi_done(struct ath10k *ar) ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); if (ret) { - ath10k_warn("unable to write to the device: %d\n", ret); + ath10k_warn(ar, "unable to write to the device: %d\n", ret); return ret; } @@ -61,10 +61,10 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, u32 resplen = sizeof(resp.get_target_info); int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n"); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n"); if (ar->bmi.done_sent) { - ath10k_warn("BMI Get Target Info Command disallowed\n"); + ath10k_warn(ar, "BMI Get Target Info Command disallowed\n"); return -EBUSY; } @@ -72,12 +72,12 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); if (ret) { - ath10k_warn("unable to get target info from device\n"); + ath10k_warn(ar, "unable to get target info from device\n"); return ret; } if (resplen < sizeof(resp.get_target_info)) { - ath10k_warn("invalid get_target_info response length (%d)\n", + ath10k_warn(ar, "invalid get_target_info response length (%d)\n", resplen); return -EIO; } @@ -97,11 +97,11 @@ int ath10k_bmi_read_memory(struct ath10k *ar, u32 rxlen; int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", address, length); if (ar->bmi.done_sent) { - ath10k_warn("command disallowed\n"); + ath10k_warn(ar, "command disallowed\n"); return -EBUSY; } @@ -115,7 +115,7 @@ int ath10k_bmi_read_memory(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &rxlen); if (ret) { - ath10k_warn("unable to read from the device (%d)\n", + ath10k_warn(ar, "unable to read from the device (%d)\n", ret); return ret; } @@ -137,11 +137,11 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 txlen; int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", address, length); if (ar->bmi.done_sent) { - ath10k_warn("command disallowed\n"); + ath10k_warn(ar, "command disallowed\n"); return -EBUSY; } @@ -159,7 +159,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, NULL, NULL); if (ret) { - ath10k_warn("unable to write to the device (%d)\n", + ath10k_warn(ar, "unable to write to the device (%d)\n", ret); return ret; } @@ -183,11 +183,11 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) u32 resplen = sizeof(resp.execute); int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", address, param); if (ar->bmi.done_sent) { - ath10k_warn("command disallowed\n"); + ath10k_warn(ar, "command disallowed\n"); return -EBUSY; } @@ -197,19 +197,19 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); if (ret) { - ath10k_warn("unable to read from the device\n"); + ath10k_warn(ar, "unable to read from the device\n"); return ret; } if (resplen < sizeof(resp.execute)) { - ath10k_warn("invalid execute response length (%d)\n", + ath10k_warn(ar, "invalid execute response length (%d)\n", resplen); return -EIO; } *result = __le32_to_cpu(resp.execute.result); - ath10k_dbg(ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result); return 0; } @@ -221,11 +221,11 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) u32 txlen; int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", buffer, length); if (ar->bmi.done_sent) { - ath10k_warn("command disallowed\n"); + ath10k_warn(ar, "command disallowed\n"); return -EBUSY; } @@ -241,7 +241,7 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, NULL, NULL); if (ret) { - ath10k_warn("unable to write to the device\n"); + ath10k_warn(ar, "unable to write to the device\n"); return ret; } @@ -258,11 +258,11 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); int ret; - ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", address); if (ar->bmi.done_sent) { - ath10k_warn("command disallowed\n"); + ath10k_warn(ar, "command disallowed\n"); return -EBUSY; } @@ -271,7 +271,7 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); if (ret) { - ath10k_warn("unable to Start LZ Stream to the device\n"); + ath10k_warn(ar, "unable to Start LZ Stream to the device\n"); return ret; } @@ -286,7 +286,7 @@ int ath10k_bmi_fast_download(struct ath10k *ar, u32 trailer_len = length - head_len; int ret; - ath10k_dbg(ATH10K_DBG_BMI, + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi fast download address 0x%x buffer 0x%p length %d\n", address, buffer, length); diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 4333107ecf37..71eef233bd01 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -284,13 +284,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, int ret = 0; if (nbytes > ce_state->src_sz_max) - ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n", + ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n", __func__, nbytes, ce_state->src_sz_max); - ret = ath10k_pci_wake(ar); - if (ret) - return ret; - if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) <= 0)) { ret = -ENOSR; @@ -325,7 +321,6 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, src_ring->write_index = write_index; exit: - ath10k_pci_sleep(ar); return ret; } @@ -390,49 +385,57 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) return delta; } -int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, - void *per_recv_context, - u32 buffer) + +int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe) { - struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; - u32 ctrl_addr = ce_state->ctrl_addr; - struct ath10k *ar = ce_state->ar; + struct ath10k *ar = pipe->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_ring *dest_ring = pipe->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; - unsigned int write_index; - unsigned int sw_index; + unsigned int write_index = dest_ring->write_index; + unsigned int sw_index = dest_ring->sw_index; + + lockdep_assert_held(&ar_pci->ce_lock); + + return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); +} + +int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) +{ + struct ath10k *ar = pipe->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_ring *dest_ring = pipe->dest_ring; + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int write_index = dest_ring->write_index; + unsigned int sw_index = dest_ring->sw_index; + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index); + u32 ctrl_addr = pipe->ctrl_addr; + + lockdep_assert_held(&ar_pci->ce_lock); + + if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0) + return -EIO; + + desc->addr = __cpu_to_le32(paddr); + desc->nbytes = 0; + + dest_ring->per_transfer_context[write_index] = ctx; + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index); + dest_ring->write_index = write_index; + + return 0; +} + +int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr) +{ + struct ath10k *ar = pipe->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; spin_lock_bh(&ar_pci->ce_lock); - write_index = dest_ring->write_index; - sw_index = dest_ring->sw_index; - - ret = ath10k_pci_wake(ar); - if (ret) - goto out; - - if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) { - struct ce_desc *base = dest_ring->base_addr_owner_space; - struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index); - - /* Update destination descriptor */ - desc->addr = __cpu_to_le32(buffer); - desc->nbytes = 0; - - dest_ring->per_transfer_context[write_index] = - per_recv_context; - - /* Update Destination Ring Write Index */ - write_index = CE_RING_IDX_INCR(nentries_mask, write_index); - ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index); - dest_ring->write_index = write_index; - ret = 0; - } else { - ret = -EIO; - } - ath10k_pci_sleep(ar); - -out: + ret = __ath10k_ce_rx_post_buf(pipe, ctx, paddr); spin_unlock_bh(&ar_pci->ce_lock); return ret; @@ -588,7 +591,6 @@ static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state, unsigned int sw_index = src_ring->sw_index; struct ce_desc *sdesc, *sbase; unsigned int read_index; - int ret; if (src_ring->hw_index == sw_index) { /* @@ -599,18 +601,12 @@ static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state, * value of the HW index has become stale. */ - ret = ath10k_pci_wake(ar); - if (ret) - return ret; - read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); if (read_index == 0xffffffff) return -ENODEV; read_index &= nentries_mask; src_ring->hw_index = read_index; - - ath10k_pci_sleep(ar); } read_index = src_ring->hw_index; @@ -731,11 +727,6 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; u32 ctrl_addr = ce_state->ctrl_addr; - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) - return; spin_lock_bh(&ar_pci->ce_lock); @@ -760,7 +751,6 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK); spin_unlock_bh(&ar_pci->ce_lock); - ath10k_pci_sleep(ar); } /* @@ -771,13 +761,9 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) void ath10k_ce_per_engine_service_any(struct ath10k *ar) { - int ce_id, ret; + int ce_id; u32 intr_summary; - ret = ath10k_pci_wake(ar); - if (ret) - return; - intr_summary = CE_INTERRUPT_SUMMARY(ar); for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) { @@ -789,8 +775,6 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) ath10k_ce_per_engine_service(ar, ce_id); } - - ath10k_pci_sleep(ar); } /* @@ -800,16 +784,11 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) * * Called with ce_lock held. */ -static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state, - int disable_copy_compl_intr) +static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state) { u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) - return; + bool disable_copy_compl_intr = ce_state->attr_flags & CE_ATTR_DIS_INTR; if ((!disable_copy_compl_intr) && (ce_state->send_cb || ce_state->recv_cb)) @@ -818,17 +797,11 @@ static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state, ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); ath10k_ce_watermark_intr_disable(ar, ctrl_addr); - - ath10k_pci_sleep(ar); } int ath10k_ce_disable_interrupts(struct ath10k *ar) { - int ce_id, ret; - - ret = ath10k_pci_wake(ar); - if (ret) - return ret; + int ce_id; for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { u32 ctrl_addr = ath10k_ce_base_address(ce_id); @@ -838,34 +811,16 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar) ath10k_ce_watermark_intr_disable(ar, ctrl_addr); } - ath10k_pci_sleep(ar); - return 0; } -void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, - void (*send_cb)(struct ath10k_ce_pipe *), - int disable_interrupts) +void ath10k_ce_enable_interrupts(struct ath10k *ar) { - struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id; - spin_lock_bh(&ar_pci->ce_lock); - ce_state->send_cb = send_cb; - ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts); - spin_unlock_bh(&ar_pci->ce_lock); -} - -void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, - void (*recv_cb)(struct ath10k_ce_pipe *)) -{ - struct ath10k *ar = ce_state->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - spin_lock_bh(&ar_pci->ce_lock); - ce_state->recv_cb = recv_cb; - ath10k_ce_per_engine_handler_adjust(ce_state, 0); - spin_unlock_bh(&ar_pci->ce_lock); + for (ce_id = 0; ce_id < CE_COUNT; ce_id++) + ath10k_ce_per_engine_handler_adjust(&ar_pci->ce_states[ce_id]); } static int ath10k_ce_init_src_ring(struct ath10k *ar, @@ -898,7 +853,7 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0); ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot init ce src ring id %d entries %d base_addr %p\n", ce_id, nentries, src_ring->base_addr_owner_space); @@ -932,7 +887,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0); ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ce dest ring id %d entries %d base_addr %p\n", ce_id, nentries, dest_ring->base_addr_owner_space); @@ -1067,7 +1022,9 @@ ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id, * initialized by software/firmware. */ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, - const struct ce_attr *attr) + const struct ce_attr *attr, + void (*send_cb)(struct ath10k_ce_pipe *), + void (*recv_cb)(struct ath10k_ce_pipe *)) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; @@ -1084,39 +1041,37 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); - ret = ath10k_pci_wake(ar); - if (ret) - return ret; - spin_lock_bh(&ar_pci->ce_lock); ce_state->ar = ar; ce_state->id = ce_id; ce_state->ctrl_addr = ath10k_ce_base_address(ce_id); ce_state->attr_flags = attr->flags; ce_state->src_sz_max = attr->src_sz_max; + if (attr->src_nentries) + ce_state->send_cb = send_cb; + if (attr->dest_nentries) + ce_state->recv_cb = recv_cb; spin_unlock_bh(&ar_pci->ce_lock); if (attr->src_nentries) { ret = ath10k_ce_init_src_ring(ar, ce_id, attr); if (ret) { - ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n", + ath10k_err(ar, "Failed to initialize CE src ring for ID: %d (%d)\n", ce_id, ret); - goto out; + return ret; } } if (attr->dest_nentries) { ret = ath10k_ce_init_dest_ring(ar, ce_id, attr); if (ret) { - ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n", + ath10k_err(ar, "Failed to initialize CE dest ring for ID: %d (%d)\n", ce_id, ret); - goto out; + return ret; } } -out: - ath10k_pci_sleep(ar); - return ret; + return 0; } static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id) @@ -1140,16 +1095,8 @@ static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id) void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id) { - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) - return; - ath10k_ce_deinit_src_ring(ar, ce_id); ath10k_ce_deinit_dest_ring(ar, ce_id); - - ath10k_pci_sleep(ar); } int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, @@ -1163,7 +1110,7 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr); if (IS_ERR(ce_state->src_ring)) { ret = PTR_ERR(ce_state->src_ring); - ath10k_err("failed to allocate copy engine source ring %d: %d\n", + ath10k_err(ar, "failed to allocate copy engine source ring %d: %d\n", ce_id, ret); ce_state->src_ring = NULL; return ret; @@ -1175,7 +1122,7 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, attr); if (IS_ERR(ce_state->dest_ring)) { ret = PTR_ERR(ce_state->dest_ring); - ath10k_err("failed to allocate copy engine destination ring %d: %d\n", + ath10k_err(ar, "failed to allocate copy engine destination ring %d: %d\n", ce_id, ret); ce_state->dest_ring = NULL; return ret; diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 7a5a36fc59c1..82d1f23546b9 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -162,30 +162,13 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe); -void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, - void (*send_cb)(struct ath10k_ce_pipe *), - int disable_interrupts); - int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe); /*==================Recv=======================*/ -/* - * Make a buffer available to receive. The buffer must be at least of a - * minimal size appropriate for this copy engine (src_sz_max attribute). - * ce - which copy engine to use - * per_transfer_recv_context - context passed back to caller's recv_cb - * buffer - address of buffer in CE space - * Returns 0 on success; otherwise an error status. - * - * Implemenation note: Pushes a buffer to Dest ring. - */ -int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, - void *per_transfer_recv_context, - u32 buffer); - -void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, - void (*recv_cb)(struct ath10k_ce_pipe *)); +int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe); +int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr); +int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr); /* recv flags */ /* Data is byte-swapped */ @@ -214,7 +197,9 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, /*==================CE Engine Initialization=======================*/ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, - const struct ce_attr *attr); + const struct ce_attr *attr, + void (*send_cb)(struct ath10k_ce_pipe *), + void (*recv_cb)(struct ath10k_ce_pipe *)); void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id); int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, const struct ce_attr *attr); @@ -245,6 +230,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, void ath10k_ce_per_engine_service_any(struct ath10k *ar); void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id); int ath10k_ce_disable_interrupts(struct ath10k *ar); +void ath10k_ce_enable_interrupts(struct ath10k *ar); /* ce_attr.flags values */ /* Use NonSnooping PCIe accesses? */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 93adb8c58969..651a6da8adf5 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -53,7 +53,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { static void ath10k_send_suspend_complete(struct ath10k *ar) { - ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot suspend complete\n"); complete(&ar->target_suspend); } @@ -67,14 +67,14 @@ static int ath10k_init_configure_target(struct ath10k *ar) ret = ath10k_bmi_write32(ar, hi_app_host_interest, HTC_PROTOCOL_VERSION); if (ret) { - ath10k_err("settings HTC version failed\n"); + ath10k_err(ar, "settings HTC version failed\n"); return ret; } /* set the firmware mode to STA/IBSS/AP */ ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m_host); if (ret) { - ath10k_err("setting firmware mode (1/2) failed\n"); + ath10k_err(ar, "setting firmware mode (1/2) failed\n"); return ret; } @@ -93,14 +93,14 @@ static int ath10k_init_configure_target(struct ath10k *ar) ret = ath10k_bmi_write32(ar, hi_option_flag, param_host); if (ret) { - ath10k_err("setting firmware mode (2/2) failed\n"); + ath10k_err(ar, "setting firmware mode (2/2) failed\n"); return ret; } /* We do all byte-swapping on the host */ ret = ath10k_bmi_write32(ar, hi_be, 0); if (ret) { - ath10k_err("setting host CPU BE mode failed\n"); + ath10k_err(ar, "setting host CPU BE mode failed\n"); return ret; } @@ -108,7 +108,7 @@ static int ath10k_init_configure_target(struct ath10k *ar) ret = ath10k_bmi_write32(ar, hi_fw_swap, 0); if (ret) { - ath10k_err("setting FW data/desc swap flags failed\n"); + ath10k_err(ar, "setting FW data/desc swap flags failed\n"); return ret; } @@ -146,11 +146,12 @@ static int ath10k_push_board_ext_data(struct ath10k *ar) ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr); if (ret) { - ath10k_err("could not read board ext data addr (%d)\n", ret); + ath10k_err(ar, "could not read board ext data addr (%d)\n", + ret); return ret; } - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot push board extended data addr 0x%x\n", board_ext_data_addr); @@ -158,7 +159,7 @@ static int ath10k_push_board_ext_data(struct ath10k *ar) return 0; if (ar->board_len != (board_data_size + board_ext_data_size)) { - ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n", + ath10k_err(ar, "invalid board (ext) data sizes %zu != %d+%d\n", ar->board_len, board_data_size, board_ext_data_size); return -EINVAL; } @@ -167,14 +168,15 @@ static int ath10k_push_board_ext_data(struct ath10k *ar) ar->board_data + board_data_size, board_ext_data_size); if (ret) { - ath10k_err("could not write board ext data (%d)\n", ret); + ath10k_err(ar, "could not write board ext data (%d)\n", ret); return ret; } ret = ath10k_bmi_write32(ar, hi_board_ext_data_config, (board_ext_data_size << 16) | 1); if (ret) { - ath10k_err("could not write board ext data bit (%d)\n", ret); + ath10k_err(ar, "could not write board ext data bit (%d)\n", + ret); return ret; } @@ -189,13 +191,13 @@ static int ath10k_download_board_data(struct ath10k *ar) ret = ath10k_push_board_ext_data(ar); if (ret) { - ath10k_err("could not push board ext data (%d)\n", ret); + ath10k_err(ar, "could not push board ext data (%d)\n", ret); goto exit; } ret = ath10k_bmi_read32(ar, hi_board_data, &address); if (ret) { - ath10k_err("could not read board data addr (%d)\n", ret); + ath10k_err(ar, "could not read board data addr (%d)\n", ret); goto exit; } @@ -203,13 +205,13 @@ static int ath10k_download_board_data(struct ath10k *ar) min_t(u32, board_data_size, ar->board_len)); if (ret) { - ath10k_err("could not write board data (%d)\n", ret); + ath10k_err(ar, "could not write board data (%d)\n", ret); goto exit; } ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1); if (ret) { - ath10k_err("could not write board data bit (%d)\n", ret); + ath10k_err(ar, "could not write board data bit (%d)\n", ret); goto exit; } @@ -225,30 +227,30 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) /* OTP is optional */ if (!ar->otp_data || !ar->otp_len) { - ath10k_warn("Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n", + ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n", ar->otp_data, ar->otp_len); return 0; } - ath10k_dbg(ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n", address, ar->otp_len); ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len); if (ret) { - ath10k_err("could not write otp (%d)\n", ret); + ath10k_err(ar, "could not write otp (%d)\n", ret); return ret; } ret = ath10k_bmi_execute(ar, address, 0, &result); if (ret) { - ath10k_err("could not execute otp (%d)\n", ret); + ath10k_err(ar, "could not execute otp (%d)\n", ret); return ret; } - ath10k_dbg(ATH10K_DBG_BOOT, "boot otp execute result %d\n", result); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result); if (result != 0) { - ath10k_err("otp calibration failed: %d", result); + ath10k_err(ar, "otp calibration failed: %d", result); return -EINVAL; } @@ -265,7 +267,7 @@ static int ath10k_download_fw(struct ath10k *ar) ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data, ar->firmware_len); if (ret) { - ath10k_err("could not write fw (%d)\n", ret); + ath10k_err(ar, "could not write fw (%d)\n", ret); goto exit; } @@ -302,12 +304,12 @@ static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) int ret = 0; if (ar->hw_params.fw.fw == NULL) { - ath10k_err("firmware file not defined\n"); + ath10k_err(ar, "firmware file not defined\n"); return -EINVAL; } if (ar->hw_params.fw.board == NULL) { - ath10k_err("board data file not defined"); + ath10k_err(ar, "board data file not defined"); return -EINVAL; } @@ -316,7 +318,7 @@ static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) ar->hw_params.fw.board); if (IS_ERR(ar->board)) { ret = PTR_ERR(ar->board); - ath10k_err("could not fetch board data (%d)\n", ret); + ath10k_err(ar, "could not fetch board data (%d)\n", ret); goto err; } @@ -328,7 +330,7 @@ static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) ar->hw_params.fw.fw); if (IS_ERR(ar->firmware)) { ret = PTR_ERR(ar->firmware); - ath10k_err("could not fetch firmware (%d)\n", ret); + ath10k_err(ar, "could not fetch firmware (%d)\n", ret); goto err; } @@ -344,7 +346,7 @@ static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) ar->hw_params.fw.otp); if (IS_ERR(ar->otp)) { ret = PTR_ERR(ar->otp); - ath10k_err("could not fetch otp (%d)\n", ret); + ath10k_err(ar, "could not fetch otp (%d)\n", ret); goto err; } @@ -369,7 +371,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) /* first fetch the firmware file (firmware-*.bin) */ ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); if (IS_ERR(ar->firmware)) { - ath10k_err("could not fetch firmware file '%s/%s': %ld\n", + ath10k_err(ar, "could not fetch firmware file '%s/%s': %ld\n", ar->hw_params.fw.dir, name, PTR_ERR(ar->firmware)); return PTR_ERR(ar->firmware); } @@ -381,14 +383,14 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; if (len < magic_len) { - ath10k_err("firmware file '%s/%s' too small to contain magic: %zu\n", + ath10k_err(ar, "firmware file '%s/%s' too small to contain magic: %zu\n", ar->hw_params.fw.dir, name, len); ret = -EINVAL; goto err; } if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { - ath10k_err("invalid firmware magic\n"); + ath10k_err(ar, "invalid firmware magic\n"); ret = -EINVAL; goto err; } @@ -410,7 +412,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) data += sizeof(*hdr); if (len < ie_len) { - ath10k_err("invalid length for FW IE %d (%zu < %zu)\n", + ath10k_err(ar, "invalid length for FW IE %d (%zu < %zu)\n", ie_id, len, ie_len); ret = -EINVAL; goto err; @@ -424,7 +426,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) memcpy(ar->hw->wiphy->fw_version, data, ie_len); ar->hw->wiphy->fw_version[ie_len] = '\0'; - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw version %s\n", ar->hw->wiphy->fw_version); break; @@ -434,11 +436,11 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) timestamp = (__le32 *)data; - ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw timestamp %d\n", le32_to_cpup(timestamp)); break; case ATH10K_FW_IE_FEATURES: - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found firmware features ie (%zd B)\n", ie_len); @@ -450,19 +452,19 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) break; if (data[index] & (1 << bit)) { - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "Enabling feature bit: %i\n", i); __set_bit(i, ar->fw_features); } } - ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", + ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "features", "", ar->fw_features, sizeof(ar->fw_features)); break; case ATH10K_FW_IE_FW_IMAGE: - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw image ie (%zd B)\n", ie_len); @@ -471,7 +473,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) break; case ATH10K_FW_IE_OTP_IMAGE: - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found otp image ie (%zd B)\n", ie_len); @@ -480,7 +482,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) break; default: - ath10k_warn("Unknown FW IE: %u\n", + ath10k_warn(ar, "Unknown FW IE: %u\n", le32_to_cpu(hdr->id)); break; } @@ -493,15 +495,22 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) } if (!ar->firmware_data || !ar->firmware_len) { - ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n", + ath10k_warn(ar, "No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n", ar->hw_params.fw.dir, name); ret = -ENOMEDIUM; goto err; } + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) && + !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well"); + ret = -EINVAL; + goto err; + } + /* now fetch the board file */ if (ar->hw_params.fw.board == NULL) { - ath10k_err("board data file not defined"); + ath10k_err(ar, "board data file not defined"); ret = -EINVAL; goto err; } @@ -511,7 +520,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) ar->hw_params.fw.board); if (IS_ERR(ar->board)) { ret = PTR_ERR(ar->board); - ath10k_err("could not fetch board data '%s/%s' (%d)\n", + ath10k_err(ar, "could not fetch board data '%s/%s' (%d)\n", ar->hw_params.fw.dir, ar->hw_params.fw.board, ret); goto err; @@ -531,22 +540,29 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) { int ret; + ar->fw_api = 3; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API3_FILE); + if (ret == 0) + goto success; + ar->fw_api = 2; - ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE); if (ret == 0) goto success; ar->fw_api = 1; - ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); ret = ath10k_core_fetch_firmware_api_1(ar); if (ret) return ret; success: - ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api); return 0; } @@ -557,19 +573,19 @@ static int ath10k_init_download_firmware(struct ath10k *ar) ret = ath10k_download_board_data(ar); if (ret) { - ath10k_err("failed to download board data: %d\n", ret); + ath10k_err(ar, "failed to download board data: %d\n", ret); return ret; } ret = ath10k_download_and_run_otp(ar); if (ret) { - ath10k_err("failed to run otp: %d\n", ret); + ath10k_err(ar, "failed to run otp: %d\n", ret); return ret; } ret = ath10k_download_fw(ar); if (ret) { - ath10k_err("failed to download firmware: %d\n", ret); + ath10k_err(ar, "failed to download firmware: %d\n", ret); return ret; } @@ -586,7 +602,7 @@ static int ath10k_init_uart(struct ath10k *ar) */ ret = ath10k_bmi_write32(ar, hi_serial_enable, 0); if (ret) { - ath10k_warn("could not disable UART prints (%d)\n", ret); + ath10k_warn(ar, "could not disable UART prints (%d)\n", ret); return ret; } @@ -595,24 +611,24 @@ static int ath10k_init_uart(struct ath10k *ar) ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7); if (ret) { - ath10k_warn("could not enable UART prints (%d)\n", ret); + ath10k_warn(ar, "could not enable UART prints (%d)\n", ret); return ret; } ret = ath10k_bmi_write32(ar, hi_serial_enable, 1); if (ret) { - ath10k_warn("could not enable UART prints (%d)\n", ret); + ath10k_warn(ar, "could not enable UART prints (%d)\n", ret); return ret; } /* Set the UART baud rate to 19200. */ ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200); if (ret) { - ath10k_warn("could not set the baud rate (%d)\n", ret); + ath10k_warn(ar, "could not set the baud rate (%d)\n", ret); return ret; } - ath10k_info("UART prints enabled\n"); + ath10k_info(ar, "UART prints enabled\n"); return 0; } @@ -629,14 +645,14 @@ static int ath10k_init_hw_params(struct ath10k *ar) } if (i == ARRAY_SIZE(ath10k_hw_params_list)) { - ath10k_err("Unsupported hardware version: 0x%x\n", + ath10k_err(ar, "Unsupported hardware version: 0x%x\n", ar->target_version); return -EINVAL; } ar->hw_params = *hw_params; - ath10k_dbg(ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n", ar->hw_params.name, ar->target_version); return 0; @@ -651,14 +667,14 @@ static void ath10k_core_restart(struct work_struct *work) switch (ar->state) { case ATH10K_STATE_ON: ar->state = ATH10K_STATE_RESTARTING; - del_timer_sync(&ar->scan.timeout); - ath10k_reset_scan((unsigned long)ar); + ath10k_hif_stop(ar); + ath10k_scan_finish(ar); ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: /* this can happen if driver is being unloaded * or if the crash happens during FW probing */ - ath10k_warn("cannot restart a device that hasn't been started\n"); + ath10k_warn(ar, "cannot restart a device that hasn't been started\n"); break; case ATH10K_STATE_RESTARTING: /* hw restart might be requested from multiple places */ @@ -667,7 +683,7 @@ static void ath10k_core_restart(struct work_struct *work) ar->state = ATH10K_STATE_WEDGED; /* fall through */ case ATH10K_STATE_WEDGED: - ath10k_warn("device is wedged, will not restart\n"); + ath10k_warn(ar, "device is wedged, will not restart\n"); break; } @@ -700,7 +716,7 @@ int ath10k_core_start(struct ath10k *ar) status = ath10k_htc_init(ar); if (status) { - ath10k_err("could not init HTC (%d)\n", status); + ath10k_err(ar, "could not init HTC (%d)\n", status); goto err; } @@ -710,90 +726,91 @@ int ath10k_core_start(struct ath10k *ar) status = ath10k_wmi_attach(ar); if (status) { - ath10k_err("WMI attach failed: %d\n", status); + ath10k_err(ar, "WMI attach failed: %d\n", status); goto err; } status = ath10k_htt_init(ar); if (status) { - ath10k_err("failed to init htt: %d\n", status); + ath10k_err(ar, "failed to init htt: %d\n", status); goto err_wmi_detach; } status = ath10k_htt_tx_alloc(&ar->htt); if (status) { - ath10k_err("failed to alloc htt tx: %d\n", status); + ath10k_err(ar, "failed to alloc htt tx: %d\n", status); goto err_wmi_detach; } status = ath10k_htt_rx_alloc(&ar->htt); if (status) { - ath10k_err("failed to alloc htt rx: %d\n", status); + ath10k_err(ar, "failed to alloc htt rx: %d\n", status); goto err_htt_tx_detach; } status = ath10k_hif_start(ar); if (status) { - ath10k_err("could not start HIF: %d\n", status); + ath10k_err(ar, "could not start HIF: %d\n", status); goto err_htt_rx_detach; } status = ath10k_htc_wait_target(&ar->htc); if (status) { - ath10k_err("failed to connect to HTC: %d\n", status); + ath10k_err(ar, "failed to connect to HTC: %d\n", status); goto err_hif_stop; } status = ath10k_htt_connect(&ar->htt); if (status) { - ath10k_err("failed to connect htt (%d)\n", status); + ath10k_err(ar, "failed to connect htt (%d)\n", status); goto err_hif_stop; } status = ath10k_wmi_connect(ar); if (status) { - ath10k_err("could not connect wmi: %d\n", status); + ath10k_err(ar, "could not connect wmi: %d\n", status); goto err_hif_stop; } status = ath10k_htc_start(&ar->htc); if (status) { - ath10k_err("failed to start htc: %d\n", status); + ath10k_err(ar, "failed to start htc: %d\n", status); goto err_hif_stop; } status = ath10k_wmi_wait_for_service_ready(ar); if (status <= 0) { - ath10k_warn("wmi service ready event not received"); + ath10k_warn(ar, "wmi service ready event not received"); status = -ETIMEDOUT; - goto err_htc_stop; + goto err_hif_stop; } - ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "firmware %s booted\n", ar->hw->wiphy->fw_version); status = ath10k_wmi_cmd_init(ar); if (status) { - ath10k_err("could not send WMI init command (%d)\n", status); - goto err_htc_stop; + ath10k_err(ar, "could not send WMI init command (%d)\n", + status); + goto err_hif_stop; } status = ath10k_wmi_wait_for_unified_ready(ar); if (status <= 0) { - ath10k_err("wmi unified ready event not received\n"); + ath10k_err(ar, "wmi unified ready event not received\n"); status = -ETIMEDOUT; - goto err_htc_stop; + goto err_hif_stop; } status = ath10k_htt_setup(&ar->htt); if (status) { - ath10k_err("failed to setup htt: %d\n", status); - goto err_htc_stop; + ath10k_err(ar, "failed to setup htt: %d\n", status); + goto err_hif_stop; } status = ath10k_debug_start(ar); if (status) - goto err_htc_stop; + goto err_hif_stop; if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) ar->free_vdev_map = (1 << TARGET_10X_NUM_VDEVS) - 1; @@ -802,28 +819,8 @@ int ath10k_core_start(struct ath10k *ar) INIT_LIST_HEAD(&ar->arvifs); - if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) { - ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n", - ar->hw_params.name, - ar->target_version, - ar->chip_id, - ar->hw->wiphy->fw_version, - ar->fw_api, - ar->htt.target_version_major, - ar->htt.target_version_minor); - ath10k_info("debug %d debugfs %d tracing %d dfs %d\n", - config_enabled(CONFIG_ATH10K_DEBUG), - config_enabled(CONFIG_ATH10K_DEBUGFS), - config_enabled(CONFIG_ATH10K_TRACING), - config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)); - } - - __set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags); - return 0; -err_htc_stop: - ath10k_htc_stop(&ar->htc); err_hif_stop: ath10k_hif_stop(ar); err_htt_rx_detach: @@ -845,14 +842,14 @@ int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt) ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt); if (ret) { - ath10k_warn("could not suspend target (%d)\n", ret); + ath10k_warn(ar, "could not suspend target (%d)\n", ret); return ret; } ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ); if (ret == 0) { - ath10k_warn("suspend timed out - target pause event never came\n"); + ath10k_warn(ar, "suspend timed out - target pause event never came\n"); return -ETIMEDOUT; } @@ -868,7 +865,6 @@ void ath10k_core_stop(struct ath10k *ar) ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR); ath10k_debug_stop(ar); - ath10k_htc_stop(&ar->htc); ath10k_hif_stop(ar); ath10k_htt_tx_free(&ar->htt); ath10k_htt_rx_free(&ar->htt); @@ -887,14 +883,14 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_hif_power_up(ar); if (ret) { - ath10k_err("could not start pci hif (%d)\n", ret); + ath10k_err(ar, "could not start pci hif (%d)\n", ret); return ret; } memset(&target_info, 0, sizeof(target_info)); ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) { - ath10k_err("could not get target info (%d)\n", ret); + ath10k_err(ar, "could not get target info (%d)\n", ret); ath10k_hif_power_down(ar); return ret; } @@ -904,14 +900,14 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_init_hw_params(ar); if (ret) { - ath10k_err("could not get hw params (%d)\n", ret); + ath10k_err(ar, "could not get hw params (%d)\n", ret); ath10k_hif_power_down(ar); return ret; } ret = ath10k_core_fetch_firmware_files(ar); if (ret) { - ath10k_err("could not fetch firmware files (%d)\n", ret); + ath10k_err(ar, "could not fetch firmware files (%d)\n", ret); ath10k_hif_power_down(ar); return ret; } @@ -920,13 +916,14 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_core_start(ar); if (ret) { - ath10k_err("could not init core (%d)\n", ret); + ath10k_err(ar, "could not init core (%d)\n", ret); ath10k_core_free_firmware_files(ar); ath10k_hif_power_down(ar); mutex_unlock(&ar->conf_mutex); return ret; } + ath10k_print_driver_info(ar); ath10k_core_stop(ar); mutex_unlock(&ar->conf_mutex); @@ -939,7 +936,7 @@ static int ath10k_core_check_chip_id(struct ath10k *ar) { u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV); - ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n", ar->chip_id, hw_revision); /* Check that we are not using hw1.0 (some of them have same pci id @@ -947,7 +944,7 @@ static int ath10k_core_check_chip_id(struct ath10k *ar) * due to missing hw1.0 workarounds. */ switch (hw_revision) { case QCA988X_HW_1_0_CHIP_ID_REV: - ath10k_err("ERROR: qca988x hw1.0 is not supported\n"); + ath10k_err(ar, "ERROR: qca988x hw1.0 is not supported\n"); return -EOPNOTSUPP; case QCA988X_HW_2_0_CHIP_ID_REV: @@ -955,7 +952,7 @@ static int ath10k_core_check_chip_id(struct ath10k *ar) return 0; default: - ath10k_warn("Warning: hardware revision unknown (0x%x), expect problems\n", + ath10k_warn(ar, "Warning: hardware revision unknown (0x%x), expect problems\n", ar->chip_id); return 0; } @@ -970,25 +967,33 @@ static void ath10k_core_register_work(struct work_struct *work) status = ath10k_core_probe_fw(ar); if (status) { - ath10k_err("could not probe fw (%d)\n", status); + ath10k_err(ar, "could not probe fw (%d)\n", status); goto err; } status = ath10k_mac_register(ar); if (status) { - ath10k_err("could not register to mac80211 (%d)\n", status); + ath10k_err(ar, "could not register to mac80211 (%d)\n", status); goto err_release_fw; } status = ath10k_debug_create(ar); if (status) { - ath10k_err("unable to initialize debugfs\n"); + ath10k_err(ar, "unable to initialize debugfs\n"); goto err_unregister_mac; } + status = ath10k_spectral_create(ar); + if (status) { + ath10k_err(ar, "failed to initialize spectral\n"); + goto err_debug_destroy; + } + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); return; +err_debug_destroy: + ath10k_debug_destroy(ar); err_unregister_mac: ath10k_mac_unregister(ar); err_release_fw: @@ -1008,7 +1013,7 @@ int ath10k_core_register(struct ath10k *ar, u32 chip_id) status = ath10k_core_check_chip_id(ar); if (status) { - ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); + ath10k_err(ar, "Unsupported chip id 0x%08x\n", ar->chip_id); return status; } @@ -1025,6 +1030,12 @@ void ath10k_core_unregister(struct ath10k *ar) if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) return; + /* Stop spectral before unregistering from mac80211 to remove the + * relayfs debugfs file cleanly. Otherwise the parent debugfs tree + * would be already be free'd recursively, leading to a double free. + */ + ath10k_spectral_destroy(ar); + /* We must unregister from mac80211 before we stop HTC and HIF. * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ @@ -1036,12 +1047,12 @@ void ath10k_core_unregister(struct ath10k *ar) } EXPORT_SYMBOL(ath10k_core_unregister); -struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, +struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, const struct ath10k_hif_ops *hif_ops) { struct ath10k *ar; - ar = ath10k_mac_create(); + ar = ath10k_mac_create(priv_size); if (!ar) return NULL; @@ -1051,7 +1062,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, ar->p2p = !!ath10k_p2p; ar->dev = dev; - ar->hif.priv = hif_priv; ar->hif.ops = hif_ops; init_completion(&ar->scan.started); @@ -1062,7 +1072,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); - setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work); ar->workqueue = create_singlethread_workqueue("ath10k_wq"); if (!ar->workqueue) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 83a5fa91531d..4ef476099225 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "htt.h" #include "htc.h" @@ -31,6 +33,7 @@ #include "../ath.h" #include "../regd.h" #include "../dfs_pattern_detector.h" +#include "spectral.h" #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -237,6 +240,7 @@ struct ath10k_vif { bool is_started; bool is_up; + bool spectral_enabled; u32 aid; u8 bssid[ETH_ALEN]; @@ -276,11 +280,20 @@ struct ath10k_vif_iter { struct ath10k_vif *arvif; }; +/* used for crash-dump storage, protected by data-lock */ +struct ath10k_fw_crash_data { + bool crashed_since_read; + + uuid_le uuid; + struct timespec timestamp; + __le32 registers[REG_DUMP_COUNT_QCA988X]; +}; + struct ath10k_debug { struct dentry *debugfs_phy; struct ath10k_target_stats target_stats; - u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_BM_SIZE); struct completion event_stats_compl; @@ -293,6 +306,8 @@ struct ath10k_debug { u8 htt_max_amsdu; u8 htt_max_ampdu; + + struct ath10k_fw_crash_data *fw_crash_data; }; enum ath10k_state { @@ -330,6 +345,11 @@ enum ath10k_fw_features { /* Firmware does not support P2P */ ATH10K_FW_FEATURE_NO_P2P = 3, + /* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature bit + * is required to be set as well. + */ + ATH10K_FW_FEATURE_WMI_10_2 = 4, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; @@ -337,10 +357,32 @@ enum ath10k_fw_features { enum ath10k_dev_flags { /* Indicates that ath10k device is during CAC phase of DFS */ ATH10K_CAC_RUNNING, - ATH10K_FLAG_FIRST_BOOT_DONE, ATH10K_FLAG_CORE_REGISTERED, }; +enum ath10k_scan_state { + ATH10K_SCAN_IDLE, + ATH10K_SCAN_STARTING, + ATH10K_SCAN_RUNNING, + ATH10K_SCAN_ABORTING, +}; + +static inline const char *ath10k_scan_state_str(enum ath10k_scan_state state) +{ + switch (state) { + case ATH10K_SCAN_IDLE: + return "idle"; + case ATH10K_SCAN_STARTING: + return "starting"; + case ATH10K_SCAN_RUNNING: + return "running"; + case ATH10K_SCAN_ABORTING: + return "aborting"; + } + + return "unknown"; +} + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -368,7 +410,6 @@ struct ath10k { bool p2p; struct { - void *priv; const struct ath10k_hif_ops *ops; } hif; @@ -410,10 +451,9 @@ struct ath10k { struct completion started; struct completion completed; struct completion on_channel; - struct timer_list timeout; + struct delayed_work timeout; + enum ath10k_scan_state state; bool is_roc; - bool in_progress; - bool aborting; int vdev_id; int roc_freq; } scan; @@ -494,9 +534,21 @@ struct ath10k { #ifdef CONFIG_ATH10K_DEBUGFS struct ath10k_debug debug; #endif + + struct { + /* relay(fs) channel for spectral scan */ + struct rchan *rfs_chan_spec_scan; + + /* spectral_mode and spec_config are protected by conf_mutex */ + enum ath10k_spectral_mode mode; + struct ath10k_spec_scan config; + } spectral; + + /* must be last */ + u8 drv_priv[0] __aligned(sizeof(void *)); }; -struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, +struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, const struct ath10k_hif_ops *hif_ops); void ath10k_core_destroy(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 3030158c478e..f3f0a80f8bab 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -17,6 +17,9 @@ #include #include +#include +#include +#include #include "core.h" #include "debug.h" @@ -24,25 +27,86 @@ /* ms */ #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000 -static int ath10k_printk(const char *level, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - int rtn; +#define ATH10K_FW_CRASH_DUMP_VERSION 1 - va_start(args, fmt); +/** + * enum ath10k_fw_crash_dump_type - types of data in the dump file + * @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format + */ +enum ath10k_fw_crash_dump_type { + ATH10K_FW_CRASH_DUMP_REGISTERS = 0, - vaf.fmt = fmt; - vaf.va = &args; + ATH10K_FW_CRASH_DUMP_MAX, +}; - rtn = printk("%sath10k: %pV", level, &vaf); +struct ath10k_tlv_dump_data { + /* see ath10k_fw_crash_dump_type above */ + __le32 type; - va_end(args); + /* in bytes */ + __le32 tlv_len; - return rtn; -} + /* pad to 32-bit boundaries as needed */ + u8 tlv_data[]; +} __packed; -int ath10k_info(const char *fmt, ...) +struct ath10k_dump_file_data { + /* dump file information */ + + /* "ATH10K-FW-DUMP" */ + char df_magic[16]; + + __le32 len; + + /* file dump version */ + __le32 version; + + /* some info we can get from ath10k struct that might help */ + + u8 uuid[16]; + + __le32 chip_id; + + /* 0 for now, in place for later hardware */ + __le32 bus_type; + + __le32 target_version; + __le32 fw_version_major; + __le32 fw_version_minor; + __le32 fw_version_release; + __le32 fw_version_build; + __le32 phy_capability; + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + __le32 ht_cap_info; + __le32 vht_cap_info; + __le32 num_rf_chains; + + /* firmware version string */ + char fw_ver[ETHTOOL_FWVERS_LEN]; + + /* Kernel related information */ + + /* time-of-day stamp */ + __le64 tv_sec; + + /* time-of-day stamp, nano-seconds */ + __le64 tv_nsec; + + /* LINUX_VERSION_CODE */ + __le32 kernel_ver_code; + + /* VERMAGIC_STRING */ + char kernel_ver[64]; + + /* room for growth w/out changing binary format */ + u8 unused[128]; + + /* struct ath10k_tlv_dump_data + more */ + u8 data[0]; +} __packed; + +int ath10k_info(struct ath10k *ar, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, @@ -52,7 +116,7 @@ int ath10k_info(const char *fmt, ...) va_start(args, fmt); vaf.va = &args; - ret = ath10k_printk(KERN_INFO, "%pV", &vaf); + ret = dev_info(ar->dev, "%pV", &vaf); trace_ath10k_log_info(&vaf); va_end(args); @@ -60,7 +124,25 @@ int ath10k_info(const char *fmt, ...) } EXPORT_SYMBOL(ath10k_info); -int ath10k_err(const char *fmt, ...) +void ath10k_print_driver_info(struct ath10k *ar) +{ + ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n", + ar->hw_params.name, + ar->target_version, + ar->chip_id, + ar->hw->wiphy->fw_version, + ar->fw_api, + ar->htt.target_version_major, + ar->htt.target_version_minor); + ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d\n", + config_enabled(CONFIG_ATH10K_DEBUG), + config_enabled(CONFIG_ATH10K_DEBUGFS), + config_enabled(CONFIG_ATH10K_TRACING), + config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)); +} +EXPORT_SYMBOL(ath10k_print_driver_info); + +int ath10k_err(struct ath10k *ar, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, @@ -70,7 +152,7 @@ int ath10k_err(const char *fmt, ...) va_start(args, fmt); vaf.va = &args; - ret = ath10k_printk(KERN_ERR, "%pV", &vaf); + ret = dev_err(ar->dev, "%pV", &vaf); trace_ath10k_log_err(&vaf); va_end(args); @@ -78,25 +160,21 @@ int ath10k_err(const char *fmt, ...) } EXPORT_SYMBOL(ath10k_err); -int ath10k_warn(const char *fmt, ...) +int ath10k_warn(struct ath10k *ar, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; - int ret = 0; va_start(args, fmt); vaf.va = &args; - - if (net_ratelimit()) - ret = ath10k_printk(KERN_WARNING, "%pV", &vaf); - + dev_warn_ratelimited(ar->dev, "%pV", &vaf); trace_ath10k_log_warn(&vaf); va_end(args); - return ret; + return 0; } EXPORT_SYMBOL(ath10k_warn); @@ -115,9 +193,10 @@ static ssize_t ath10k_read_wmi_services(struct file *file, { struct ath10k *ar = file->private_data; char *buf; - unsigned int len = 0, buf_len = 1500; - const char *status; + unsigned int len = 0, buf_len = 4096; + const char *name; ssize_t ret_cnt; + bool enabled; int i; buf = kzalloc(buf_len, GFP_KERNEL); @@ -129,15 +208,22 @@ static ssize_t ath10k_read_wmi_services(struct file *file, if (len > buf_len) len = buf_len; - for (i = 0; i < WMI_SERVICE_LAST; i++) { - if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i)) - status = "enabled"; - else - status = "disabled"; + for (i = 0; i < WMI_MAX_SERVICE; i++) { + enabled = test_bit(i, ar->debug.wmi_service_bitmap); + name = wmi_service_name(i); + + if (!name) { + if (enabled) + len += scnprintf(buf + len, buf_len - len, + "%-40s %s (bit %d)\n", + "unknown", "enabled", i); + + continue; + } len += scnprintf(buf + len, buf_len - len, - "0x%02x - %20s - %s\n", - i, wmi_service_name(i), status); + "%-40s %s\n", + name, enabled ? "enabled" : "-"); } ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); @@ -309,7 +395,7 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT); if (ret) { - ath10k_warn("could not request stats (%d)\n", ret); + ath10k_warn(ar, "could not request stats (%d)\n", ret); goto exit; } @@ -527,11 +613,14 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file, } if (!strcmp(buf, "soft")) { - ath10k_info("simulating soft firmware crash\n"); + ath10k_info(ar, "simulating soft firmware crash\n"); ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0); } else if (!strcmp(buf, "hard")) { - ath10k_info("simulating hard firmware crash\n"); - ret = ath10k_wmi_vdev_set_param(ar, TARGET_NUM_VDEVS + 1, + ath10k_info(ar, "simulating hard firmware crash\n"); + /* 0x7fff is vdev id, and it is always out of range for all + * firmware variants in order to force a firmware crash. + */ + ret = ath10k_wmi_vdev_set_param(ar, 0x7fff, ar->wmi.vdev_param->rts_threshold, 0); } else { ret = -EINVAL; @@ -539,7 +628,7 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file, } if (ret) { - ath10k_warn("failed to simulate firmware crash: %d\n", ret); + ath10k_warn(ar, "failed to simulate firmware crash: %d\n", ret); goto exit; } @@ -577,6 +666,138 @@ static const struct file_operations fops_chip_id = { .llseek = default_llseek, }; +struct ath10k_fw_crash_data * +ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) +{ + struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data; + + lockdep_assert_held(&ar->data_lock); + + crash_data->crashed_since_read = true; + uuid_le_gen(&crash_data->uuid); + getnstimeofday(&crash_data->timestamp); + + return crash_data; +} +EXPORT_SYMBOL(ath10k_debug_get_new_fw_crash_data); + +static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar) +{ + struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data; + struct ath10k_dump_file_data *dump_data; + struct ath10k_tlv_dump_data *dump_tlv; + int hdr_len = sizeof(*dump_data); + unsigned int len, sofar = 0; + unsigned char *buf; + + len = hdr_len; + len += sizeof(*dump_tlv) + sizeof(crash_data->registers); + + sofar += hdr_len; + + /* This is going to get big when we start dumping FW RAM and such, + * so go ahead and use vmalloc. + */ + buf = vzalloc(len); + if (!buf) + return NULL; + + spin_lock_bh(&ar->data_lock); + + if (!crash_data->crashed_since_read) { + spin_unlock_bh(&ar->data_lock); + vfree(buf); + return NULL; + } + + dump_data = (struct ath10k_dump_file_data *)(buf); + strlcpy(dump_data->df_magic, "ATH10K-FW-DUMP", + sizeof(dump_data->df_magic)); + dump_data->len = cpu_to_le32(len); + + dump_data->version = cpu_to_le32(ATH10K_FW_CRASH_DUMP_VERSION); + + memcpy(dump_data->uuid, &crash_data->uuid, sizeof(dump_data->uuid)); + dump_data->chip_id = cpu_to_le32(ar->chip_id); + dump_data->bus_type = cpu_to_le32(0); + dump_data->target_version = cpu_to_le32(ar->target_version); + dump_data->fw_version_major = cpu_to_le32(ar->fw_version_major); + dump_data->fw_version_minor = cpu_to_le32(ar->fw_version_minor); + dump_data->fw_version_release = cpu_to_le32(ar->fw_version_release); + dump_data->fw_version_build = cpu_to_le32(ar->fw_version_build); + dump_data->phy_capability = cpu_to_le32(ar->phy_capability); + dump_data->hw_min_tx_power = cpu_to_le32(ar->hw_min_tx_power); + dump_data->hw_max_tx_power = cpu_to_le32(ar->hw_max_tx_power); + dump_data->ht_cap_info = cpu_to_le32(ar->ht_cap_info); + dump_data->vht_cap_info = cpu_to_le32(ar->vht_cap_info); + dump_data->num_rf_chains = cpu_to_le32(ar->num_rf_chains); + + strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version, + sizeof(dump_data->fw_ver)); + + dump_data->kernel_ver_code = cpu_to_le32(LINUX_VERSION_CODE); + strlcpy(dump_data->kernel_ver, VERMAGIC_STRING, + sizeof(dump_data->kernel_ver)); + + dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec); + dump_data->tv_nsec = cpu_to_le64(crash_data->timestamp.tv_nsec); + + /* Gather crash-dump */ + dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar); + dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_REGISTERS); + dump_tlv->tlv_len = cpu_to_le32(sizeof(crash_data->registers)); + memcpy(dump_tlv->tlv_data, &crash_data->registers, + sizeof(crash_data->registers)); + sofar += sizeof(*dump_tlv) + sizeof(crash_data->registers); + + ar->debug.fw_crash_data->crashed_since_read = false; + + spin_unlock_bh(&ar->data_lock); + + return dump_data; +} + +static int ath10k_fw_crash_dump_open(struct inode *inode, struct file *file) +{ + struct ath10k *ar = inode->i_private; + struct ath10k_dump_file_data *dump; + + dump = ath10k_build_dump_file(ar); + if (!dump) + return -ENODATA; + + file->private_data = dump; + + return 0; +} + +static ssize_t ath10k_fw_crash_dump_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k_dump_file_data *dump_file = file->private_data; + + return simple_read_from_buffer(user_buf, count, ppos, + dump_file, + le32_to_cpu(dump_file->len)); +} + +static int ath10k_fw_crash_dump_release(struct inode *inode, + struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static const struct file_operations fops_fw_crash_dump = { + .open = ath10k_fw_crash_dump_open, + .read = ath10k_fw_crash_dump_read, + .release = ath10k_fw_crash_dump_release, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static int ath10k_debug_htt_stats_req(struct ath10k *ar) { u64 cookie; @@ -596,7 +817,7 @@ static int ath10k_debug_htt_stats_req(struct ath10k *ar) ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask, cookie); if (ret) { - ath10k_warn("failed to send htt stats request: %d\n", ret); + ath10k_warn(ar, "failed to send htt stats request: %d\n", ret); return ret; } @@ -770,7 +991,7 @@ static ssize_t ath10k_write_fw_dbglog(struct file *file, if (ar->state == ATH10K_STATE_ON) { ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); if (ret) { - ath10k_warn("dbglog cfg failed from debugfs: %d\n", + ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n", ret); goto exit; } @@ -801,13 +1022,14 @@ int ath10k_debug_start(struct ath10k *ar) ret = ath10k_debug_htt_stats_req(ar); if (ret) /* continue normally anyway, this isn't serious */ - ath10k_warn("failed to start htt stats workqueue: %d\n", ret); + ath10k_warn(ar, "failed to start htt stats workqueue: %d\n", + ret); if (ar->debug.fw_dbglog_mask) { ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); if (ret) /* not serious */ - ath10k_warn("failed to enable dbglog during start: %d", + ath10k_warn(ar, "failed to enable dbglog during start: %d", ret); } @@ -910,11 +1132,20 @@ static const struct file_operations fops_dfs_stats = { int ath10k_debug_create(struct ath10k *ar) { + int ret; + + ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data)); + if (!ar->debug.fw_crash_data) { + ret = -ENOMEM; + goto err; + } + ar->debug.debugfs_phy = debugfs_create_dir("ath10k", ar->hw->wiphy->debugfsdir); - - if (!ar->debug.debugfs_phy) - return -ENOMEM; + if (!ar->debug.debugfs_phy) { + ret = -ENOMEM; + goto err_free_fw_crash_data; + } INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork, ath10k_debug_htt_stats_dwork); @@ -930,6 +1161,9 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash); + debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_fw_crash_dump); + debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_chip_id); @@ -958,17 +1192,25 @@ int ath10k_debug_create(struct ath10k *ar) } return 0; + +err_free_fw_crash_data: + vfree(ar->debug.fw_crash_data); + +err: + return ret; } void ath10k_debug_destroy(struct ath10k *ar) { + vfree(ar->debug.fw_crash_data); cancel_delayed_work_sync(&ar->debug.htt_stats_dwork); } #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG -void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...) +void ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask, + const char *fmt, ...) { struct va_format vaf; va_list args; @@ -979,7 +1221,7 @@ void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...) vaf.va = &args; if (ath10k_debug_mask & mask) - ath10k_printk(KERN_DEBUG, "%pV", &vaf); + dev_printk(KERN_DEBUG, ar->dev, "%pV", &vaf); trace_ath10k_log_dbg(mask, &vaf); @@ -987,13 +1229,14 @@ void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...) } EXPORT_SYMBOL(ath10k_dbg); -void ath10k_dbg_dump(enum ath10k_debug_mask mask, +void ath10k_dbg_dump(struct ath10k *ar, + enum ath10k_debug_mask mask, const char *msg, const char *prefix, const void *buf, size_t len) { if (ath10k_debug_mask & mask) { if (msg) - ath10k_dbg(mask, "%s\n", msg); + ath10k_dbg(ar, mask, "%s\n", msg); print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); } diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index a5824990bd2a..56746539bea2 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -39,9 +39,10 @@ enum ath10k_debug_mask { extern unsigned int ath10k_debug_mask; -__printf(1, 2) int ath10k_info(const char *fmt, ...); -__printf(1, 2) int ath10k_err(const char *fmt, ...); -__printf(1, 2) int ath10k_warn(const char *fmt, ...); +__printf(2, 3) int ath10k_info(struct ath10k *ar, const char *fmt, ...); +__printf(2, 3) int ath10k_err(struct ath10k *ar, const char *fmt, ...); +__printf(2, 3) int ath10k_warn(struct ath10k *ar, const char *fmt, ...); +void ath10k_print_driver_info(struct ath10k *ar); #ifdef CONFIG_ATH10K_DEBUGFS int ath10k_debug_start(struct ath10k *ar); @@ -53,6 +54,10 @@ void ath10k_debug_read_service_map(struct ath10k *ar, size_t map_size); void ath10k_debug_read_target_stats(struct ath10k *ar, struct wmi_stats_event *ev); +struct ath10k_fw_crash_data * +ath10k_debug_get_new_fw_crash_data(struct ath10k *ar); + +void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len); #define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++) @@ -86,25 +91,40 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar, { } +static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, + int len) +{ +} + +static inline struct ath10k_fw_crash_data * +ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) +{ + return NULL; +} + #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG -__printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, +__printf(3, 4) void ath10k_dbg(struct ath10k *ar, + enum ath10k_debug_mask mask, const char *fmt, ...); -void ath10k_dbg_dump(enum ath10k_debug_mask mask, +void ath10k_dbg_dump(struct ath10k *ar, + enum ath10k_debug_mask mask, const char *msg, const char *prefix, const void *buf, size_t len); #else /* CONFIG_ATH10K_DEBUG */ -static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask, +static inline int ath10k_dbg(struct ath10k *ar, + enum ath10k_debug_mask dbg_mask, const char *fmt, ...) { return 0; } -static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask, +static inline void ath10k_dbg_dump(struct ath10k *ar, + enum ath10k_debug_mask mask, const char *msg, const char *prefix, const void *buf, size_t len) { diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 5fdc40d3b378..fd9a251f0659 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -46,7 +46,7 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE); if (!skb) { - ath10k_warn("Unable to allocate ctrl skb\n"); + ath10k_warn(ar, "Unable to allocate ctrl skb\n"); return NULL; } @@ -56,7 +56,7 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) skb_cb = ATH10K_SKB_CB(skb); memset(skb_cb, 0, sizeof(*skb_cb)); - ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb); + ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb); return skb; } @@ -72,13 +72,15 @@ static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc, static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, + struct ath10k *ar = ep->htc->ar; + + ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, ep->eid, skb); ath10k_htc_restore_tx_skb(ep->htc, skb); if (!ep->ep_ops.ep_tx_complete) { - ath10k_warn("no tx handler for eid %d\n", ep->eid); + ath10k_warn(ar, "no tx handler for eid %d\n", ep->eid); dev_kfree_skb_any(skb); return; } @@ -89,12 +91,14 @@ static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, /* assumes tx_lock is held */ static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep) { + struct ath10k *ar = ep->htc->ar; + if (!ep->tx_credit_flow_enabled) return false; if (ep->tx_credits >= ep->tx_credits_per_max_message) return false; - ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n", + ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n", ep->eid); return true; } @@ -123,6 +127,7 @@ int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *skb) { + struct ath10k *ar = htc->ar; struct ath10k_htc_ep *ep = &htc->endpoint[eid]; struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct ath10k_hif_sg_item sg_item; @@ -134,18 +139,10 @@ int ath10k_htc_send(struct ath10k_htc *htc, return -ECOMM; if (eid >= ATH10K_HTC_EP_COUNT) { - ath10k_warn("Invalid endpoint id: %d\n", eid); + ath10k_warn(ar, "Invalid endpoint id: %d\n", eid); return -ENOENT; } - /* FIXME: This looks ugly, can we fix it? */ - spin_lock_bh(&htc->tx_lock); - if (htc->stopped) { - spin_unlock_bh(&htc->tx_lock); - return -ESHUTDOWN; - } - spin_unlock_bh(&htc->tx_lock); - skb_push(skb, sizeof(struct ath10k_htc_hdr)); if (ep->tx_credit_flow_enabled) { @@ -157,7 +154,7 @@ int ath10k_htc_send(struct ath10k_htc *htc, goto err_pull; } ep->tx_credits -= credits; - ath10k_dbg(ATH10K_DBG_HTC, + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc ep %d consumed %d credits (total %d)\n", eid, credits, ep->tx_credits); spin_unlock_bh(&htc->tx_lock); @@ -188,7 +185,7 @@ int ath10k_htc_send(struct ath10k_htc *htc, if (ep->tx_credit_flow_enabled) { spin_lock_bh(&htc->tx_lock); ep->tx_credits += credits; - ath10k_dbg(ATH10K_DBG_HTC, + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc ep %d reverted %d credits back (total %d)\n", eid, credits, ep->tx_credits); spin_unlock_bh(&htc->tx_lock); @@ -227,11 +224,12 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc, int len, enum ath10k_htc_ep_id eid) { + struct ath10k *ar = htc->ar; struct ath10k_htc_ep *ep; int i, n_reports; if (len % sizeof(*report)) - ath10k_warn("Uneven credit report len %d", len); + ath10k_warn(ar, "Uneven credit report len %d", len); n_reports = len / sizeof(*report); @@ -243,7 +241,7 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc, ep = &htc->endpoint[report->eid]; ep->tx_credits += report->credits; - ath10k_dbg(ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n", + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n", report->eid, report->credits, ep->tx_credits); if (ep->ep_ops.ep_tx_credits) { @@ -260,6 +258,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, int length, enum ath10k_htc_ep_id src_eid) { + struct ath10k *ar = htc->ar; int status = 0; struct ath10k_htc_record *record; u8 *orig_buffer; @@ -279,7 +278,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, if (record->hdr.len > length) { /* no room left in buffer for record */ - ath10k_warn("Invalid record length: %d\n", + ath10k_warn(ar, "Invalid record length: %d\n", record->hdr.len); status = -EINVAL; break; @@ -289,7 +288,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, case ATH10K_HTC_RECORD_CREDITS: len = sizeof(struct ath10k_htc_credit_report); if (record->hdr.len < len) { - ath10k_warn("Credit report too long\n"); + ath10k_warn(ar, "Credit report too long\n"); status = -EINVAL; break; } @@ -299,7 +298,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, src_eid); break; default: - ath10k_warn("Unhandled record: id:%d length:%d\n", + ath10k_warn(ar, "Unhandled record: id:%d length:%d\n", record->hdr.id, record->hdr.len); break; } @@ -313,7 +312,7 @@ static int ath10k_htc_process_trailer(struct ath10k_htc *htc, } if (status) - ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "", + ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc rx bad trailer", "", orig_buffer, orig_length); return status; @@ -339,8 +338,8 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, eid = hdr->eid; if (eid >= ATH10K_HTC_EP_COUNT) { - ath10k_warn("HTC Rx: invalid eid %d\n", eid); - ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "", + ath10k_warn(ar, "HTC Rx: invalid eid %d\n", eid); + ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad header", "", hdr, sizeof(*hdr)); status = -EINVAL; goto out; @@ -360,19 +359,19 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, payload_len = __le16_to_cpu(hdr->len); if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) { - ath10k_warn("HTC rx frame too long, len: %zu\n", + ath10k_warn(ar, "HTC rx frame too long, len: %zu\n", payload_len + sizeof(*hdr)); - ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "", + ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "", hdr, sizeof(*hdr)); status = -EINVAL; goto out; } if (skb->len < payload_len) { - ath10k_dbg(ATH10K_DBG_HTC, + ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC Rx: insufficient length, got %d, expected %d\n", skb->len, payload_len); - ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", + ath10k_dbg_dump(ar, ATH10K_DBG_HTC, "htc bad rx pkt len", "", hdr, sizeof(*hdr)); status = -EINVAL; goto out; @@ -388,7 +387,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, if ((trailer_len < min_len) || (trailer_len > payload_len)) { - ath10k_warn("Invalid trailer length: %d\n", + ath10k_warn(ar, "Invalid trailer length: %d\n", trailer_len); status = -EPROTO; goto out; @@ -421,7 +420,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, * this is a fatal error, target should not be * sending unsolicited messages on the ep 0 */ - ath10k_warn("HTC rx ctrl still processing\n"); + ath10k_warn(ar, "HTC rx ctrl still processing\n"); status = -EINVAL; complete(&htc->ctl_resp); goto out; @@ -442,7 +441,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar, goto out; } - ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n", + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n", eid, skb); ep->ep_ops.ep_rx_complete(ar, skb); @@ -459,7 +458,7 @@ static void ath10k_htc_control_rx_complete(struct ath10k *ar, { /* This is unexpected. FW is not supposed to send regular rx on this * endpoint. */ - ath10k_warn("unexpected htc rx\n"); + ath10k_warn(ar, "unexpected htc rx\n"); kfree_skb(skb); } @@ -546,6 +545,7 @@ static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc, int ath10k_htc_wait_target(struct ath10k_htc *htc) { + struct ath10k *ar = htc->ar; int i, status = 0; struct ath10k_htc_svc_conn_req conn_req; struct ath10k_htc_svc_conn_resp conn_resp; @@ -563,7 +563,7 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) * iomap writes unmasking PCI CE irqs aren't propagated * properly in KVM PCI-passthrough sometimes. */ - ath10k_warn("failed to receive control response completion, polling..\n"); + ath10k_warn(ar, "failed to receive control response completion, polling..\n"); for (i = 0; i < CE_COUNT; i++) ath10k_hif_send_complete_check(htc->ar, i, 1); @@ -576,12 +576,12 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) } if (status < 0) { - ath10k_err("ctl_resp never came in (%d)\n", status); + ath10k_err(ar, "ctl_resp never came in (%d)\n", status); return status; } if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) { - ath10k_err("Invalid HTC ready msg len:%d\n", + ath10k_err(ar, "Invalid HTC ready msg len:%d\n", htc->control_resp_len); return -ECOMM; } @@ -592,21 +592,21 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) credit_size = __le16_to_cpu(msg->ready.credit_size); if (message_id != ATH10K_HTC_MSG_READY_ID) { - ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id); + ath10k_err(ar, "Invalid HTC ready msg: 0x%x\n", message_id); return -ECOMM; } htc->total_transmit_credits = credit_count; htc->target_credit_size = credit_size; - ath10k_dbg(ATH10K_DBG_HTC, + ath10k_dbg(ar, ATH10K_DBG_HTC, "Target ready! transmit resources: %d size:%d\n", htc->total_transmit_credits, htc->target_credit_size); if ((htc->total_transmit_credits == 0) || (htc->target_credit_size == 0)) { - ath10k_err("Invalid credit size received\n"); + ath10k_err(ar, "Invalid credit size received\n"); return -ECOMM; } @@ -623,7 +623,8 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) /* connect fake service */ status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp); if (status) { - ath10k_err("could not connect to htc service (%d)\n", status); + ath10k_err(ar, "could not connect to htc service (%d)\n", + status); return status; } @@ -634,6 +635,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, struct ath10k_htc_svc_conn_req *conn_req, struct ath10k_htc_svc_conn_resp *conn_resp) { + struct ath10k *ar = htc->ar; struct ath10k_htc_msg *msg; struct ath10k_htc_conn_svc *req_msg; struct ath10k_htc_conn_svc_response resp_msg_dummy; @@ -659,13 +661,13 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, tx_alloc = ath10k_htc_get_credit_allocation(htc, conn_req->service_id); if (!tx_alloc) - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service %s does not allocate target credits\n", htc_service_name(conn_req->service_id)); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); if (!skb) { - ath10k_err("Failed to allocate HTC packet\n"); + ath10k_err(ar, "Failed to allocate HTC packet\n"); return -ENOMEM; } @@ -703,7 +705,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, if (status <= 0) { if (status == 0) status = -ETIMEDOUT; - ath10k_err("Service connect timeout: %d\n", status); + ath10k_err(ar, "Service connect timeout: %d\n", status); return status; } @@ -716,11 +718,11 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->connect_service_response))) { - ath10k_err("Invalid resp message ID 0x%x", message_id); + ath10k_err(ar, "Invalid resp message ID 0x%x", message_id); return -EPROTO; } - ath10k_dbg(ATH10K_DBG_HTC, + ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n", htc_service_name(service_id), resp_msg->status, resp_msg->eid); @@ -729,7 +731,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, /* check response status */ if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) { - ath10k_err("HTC Service %s connect request failed: 0x%x)\n", + ath10k_err(ar, "HTC Service %s connect request failed: 0x%x)\n", htc_service_name(service_id), resp_msg->status); return -EPROTO; @@ -780,18 +782,18 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, if (status) return status; - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", htc_service_name(ep->service_id), ep->ul_pipe_id, ep->dl_pipe_id, ep->eid); - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc ep %d ul polled %d dl polled %d\n", ep->eid, ep->ul_is_polled, ep->dl_is_polled); if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { ep->tx_credit_flow_enabled = false; - ath10k_dbg(ATH10K_DBG_BOOT, + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service '%s' eid %d TX flow control disabled\n", htc_service_name(ep->service_id), assigned_eid); } @@ -799,13 +801,13 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, return status; } -struct sk_buff *ath10k_htc_alloc_skb(int size) +struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size) { struct sk_buff *skb; skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr)); if (!skb) { - ath10k_warn("could not allocate HTC tx skb\n"); + ath10k_warn(ar, "could not allocate HTC tx skb\n"); return NULL; } @@ -813,13 +815,14 @@ struct sk_buff *ath10k_htc_alloc_skb(int size) /* FW/HTC requires 4-byte aligned streams */ if (!IS_ALIGNED((unsigned long)skb->data, 4)) - ath10k_warn("Unaligned HTC tx skb\n"); + ath10k_warn(ar, "Unaligned HTC tx skb\n"); return skb; } int ath10k_htc_start(struct ath10k_htc *htc) { + struct ath10k *ar = htc->ar; struct sk_buff *skb; int status = 0; struct ath10k_htc_msg *msg; @@ -835,7 +838,7 @@ int ath10k_htc_start(struct ath10k_htc *htc) msg->hdr.message_id = __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID); - ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n"); + ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC is using TX credit flow control\n"); status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); if (status) { @@ -846,13 +849,6 @@ int ath10k_htc_start(struct ath10k_htc *htc) return 0; } -void ath10k_htc_stop(struct ath10k_htc *htc) -{ - spin_lock_bh(&htc->tx_lock); - htc->stopped = true; - spin_unlock_bh(&htc->tx_lock); -} - /* registered target arrival callback from the HIF layer */ int ath10k_htc_init(struct ath10k *ar) { @@ -862,7 +858,6 @@ int ath10k_htc_init(struct ath10k *ar) spin_lock_init(&htc->tx_lock); - htc->stopped = false; ath10k_htc_reset_endpoint_states(htc); /* setup HIF layer callbacks */ diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index 4716d331e6b6..bf532f671189 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -332,7 +332,7 @@ struct ath10k_htc { struct ath10k *ar; struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT]; - /* protects endpoint and stopped fields */ + /* protects endpoints */ spinlock_t tx_lock; struct ath10k_htc_ops htc_ops; @@ -345,8 +345,6 @@ struct ath10k_htc { int total_transmit_credits; struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT]; int target_credit_size; - - bool stopped; }; int ath10k_htc_init(struct ath10k *ar); @@ -357,7 +355,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, struct ath10k_htc_svc_conn_resp *conn_resp); int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *packet); -void ath10k_htc_stop(struct ath10k_htc *htc); -struct sk_buff *ath10k_htc_alloc_skb(int size); +struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 19c12cc8d663..87daae11f116 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -74,12 +74,14 @@ int ath10k_htt_init(struct ath10k *ar) static int ath10k_htt_verify_version(struct ath10k_htt *htt) { - ath10k_dbg(ATH10K_DBG_BOOT, "htt target version %d.%d\n", + struct ath10k *ar = htt->ar; + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt target version %d.%d\n", htt->target_version_major, htt->target_version_minor); if (htt->target_version_major != 2 && htt->target_version_major != 3) { - ath10k_err("unsupported htt major version %d. supported versions are 2 and 3\n", + ath10k_err(ar, "unsupported htt major version %d. supported versions are 2 and 3\n", htt->target_version_major); return -ENOTSUPP; } @@ -89,6 +91,7 @@ static int ath10k_htt_verify_version(struct ath10k_htt *htt) int ath10k_htt_setup(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; int status; init_completion(&htt->target_version_received); @@ -100,7 +103,7 @@ int ath10k_htt_setup(struct ath10k_htt *htt) status = wait_for_completion_timeout(&htt->target_version_received, HTT_TARGET_VERSION_TIMEOUT_HZ); if (status <= 0) { - ath10k_warn("htt version request timed out\n"); + ath10k_warn(ar, "htt version request timed out\n"); return -ETIMEDOUT; } diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 80cdac15588a..30927b1d7109 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -271,13 +271,14 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt) static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; int idx; struct sk_buff *msdu; lockdep_assert_held(&htt->rx_ring.lock); if (htt->rx_ring.fill_cnt == 0) { - ath10k_warn("tried to pop sk_buff from an empty rx ring\n"); + ath10k_warn(ar, "tried to pop sk_buff from an empty rx ring\n"); return NULL; } @@ -311,6 +312,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, struct sk_buff **tail_msdu, u32 *attention) { + struct ath10k *ar = htt->ar; int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; struct htt_rx_desc *rx_desc; @@ -318,7 +320,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, lockdep_assert_held(&htt->rx_ring.lock); if (htt->rx_confused) { - ath10k_warn("htt is confused. refusing rx\n"); + ath10k_warn(ar, "htt is confused. refusing rx\n"); return -1; } @@ -331,7 +333,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ", + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ", msdu->data, msdu->len + skb_tailroom(msdu)); rx_desc = (struct htt_rx_desc *)msdu->data; @@ -354,7 +356,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, ath10k_htt_rx_free_msdu_chain(*head_msdu); *head_msdu = NULL; msdu = NULL; - ath10k_err("htt rx stopped. cannot recover\n"); + ath10k_err(ar, "htt rx stopped. cannot recover\n"); htt->rx_confused = true; break; } @@ -429,7 +431,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, next->len + skb_tailroom(next), DMA_FROM_DEVICE); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx chained: ", next->data, next->len + skb_tailroom(next)); @@ -483,13 +485,14 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr) int ath10k_htt_rx_alloc(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; dma_addr_t paddr; void *vaddr; struct timer_list *timer = &htt->rx_ring.refill_retry_timer; htt->rx_ring.size = ath10k_htt_rx_ring_size(htt); if (!is_power_of_2(htt->rx_ring.size)) { - ath10k_warn("htt rx ring size is not power of 2\n"); + ath10k_warn(ar, "htt rx ring size is not power of 2\n"); return -EINVAL; } @@ -550,7 +553,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task, (unsigned long)htt); - ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n", htt->rx_ring.size, htt->rx_ring.fill_level); return 0; @@ -572,7 +575,8 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) return -ENOMEM; } -static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type) +static int ath10k_htt_rx_crypto_param_len(struct ath10k *ar, + enum htt_rx_mpdu_encrypt_type type) { switch (type) { case HTT_RX_MPDU_ENCRYPT_WEP40: @@ -588,11 +592,12 @@ static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type) return 0; } - ath10k_warn("unknown encryption type %d\n", type); + ath10k_warn(ar, "unknown encryption type %d\n", type); return 0; } -static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type) +static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar, + enum htt_rx_mpdu_encrypt_type type) { switch (type) { case HTT_RX_MPDU_ENCRYPT_NONE: @@ -608,7 +613,7 @@ static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type) return 8; } - ath10k_warn("unknown encryption type %d\n", type); + ath10k_warn(ar, "unknown encryption type %d\n", type); return 0; } @@ -819,19 +824,55 @@ static bool ath10k_htt_rx_h_channel(struct ath10k *ar, return true; } +static const char * const tid_to_ac[] = { + "BE", + "BK", + "BK", + "BE", + "VI", + "VI", + "VO", + "VO", +}; + +static char *ath10k_get_tid(struct ieee80211_hdr *hdr, char *out, size_t size) +{ + u8 *qc; + int tid; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return ""; + + qc = ieee80211_get_qos_ctl(hdr); + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + if (tid < 8) + snprintf(out, size, "tid %d (%s)", tid, tid_to_ac[tid]); + else + snprintf(out, size, "tid %d", tid); + + return out; +} + static void ath10k_process_rx(struct ath10k *ar, struct ieee80211_rx_status *rx_status, struct sk_buff *skb) { struct ieee80211_rx_status *status; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + char tid[32]; status = IEEE80211_SKB_RXCB(skb); *status = *rx_status; - ath10k_dbg(ATH10K_DBG_DATA, - "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %imic-err %i\n", + ath10k_dbg(ar, ATH10K_DBG_DATA, + "rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, skb->len, + ieee80211_get_SA(hdr), + ath10k_get_tid(hdr, tid, sizeof(tid)), + is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? + "mcast" : "ucast", + (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, status->flag == 0 ? "legacy" : "", status->flag & RX_FLAG_HT ? "ht" : "", status->flag & RX_FLAG_VHT ? "vht" : "", @@ -843,8 +884,9 @@ static void ath10k_process_rx(struct ath10k *ar, status->freq, status->band, status->flag, !!(status->flag & RX_FLAG_FAILED_FCS_CRC), - !!(status->flag & RX_FLAG_MMIC_ERROR)); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ", + !!(status->flag & RX_FLAG_MMIC_ERROR), + !!(status->flag & RX_FLAG_AMSDU_MORE)); + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ", skb->data, skb->len); ieee80211_rx(ar->hw, skb); @@ -860,13 +902,14 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, struct ieee80211_rx_status *rx_status, struct sk_buff *skb_in) { + struct ath10k *ar = htt->ar; struct htt_rx_desc *rxd; struct sk_buff *skb = skb_in; struct sk_buff *first; enum rx_msdu_decap_format fmt; enum htt_rx_mpdu_encrypt_type enctype; struct ieee80211_hdr *hdr; - u8 hdr_buf[64], addr[ETH_ALEN], *qos; + u8 hdr_buf[64], da[ETH_ALEN], sa[ETH_ALEN], *qos; unsigned int hdr_len; rxd = (void *)skb->data - sizeof(*rxd); @@ -893,8 +936,8 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, /* First frame in an A-MSDU chain has more decapped data. */ if (skb == first) { len = round_up(ieee80211_hdrlen(hdr->frame_control), 4); - len += round_up(ath10k_htt_rx_crypto_param_len(enctype), - 4); + len += round_up(ath10k_htt_rx_crypto_param_len(ar, + enctype), 4); decap_hdr += len; } @@ -904,10 +947,11 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, skb_trim(skb, skb->len - FCS_LEN); break; case RX_MSDU_DECAP_NATIVE_WIFI: - /* pull decapped header and copy DA */ + /* pull decapped header and copy SA & DA */ hdr = (struct ieee80211_hdr *)skb->data; hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr); - memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(da, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(sa, ieee80211_get_SA(hdr), ETH_ALEN); skb_pull(skb, hdr_len); /* push original 802.11 header */ @@ -921,8 +965,11 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, qos = ieee80211_get_qos_ctl(hdr); qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - /* original 802.11 header has a different DA */ - memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN); + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + memcpy(ieee80211_get_DA(hdr), da, ETH_ALEN); + memcpy(ieee80211_get_SA(hdr), sa, ETH_ALEN); break; case RX_MSDU_DECAP_ETHERNET2_DIX: /* strip ethernet header and insert decapped 802.11 @@ -965,6 +1012,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct ieee80211_rx_status *rx_status, struct sk_buff *skb) { + struct ath10k *ar = htt->ar; struct htt_rx_desc *rxd; struct ieee80211_hdr *hdr; enum rx_msdu_decap_format fmt; @@ -974,7 +1022,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, /* This shouldn't happen. If it does than it may be a FW bug. */ if (skb->next) { - ath10k_warn("htt rx received chained non A-MSDU frame\n"); + ath10k_warn(ar, "htt rx received chained non A-MSDU frame\n"); ath10k_htt_rx_free_msdu_chain(skb->next); skb->next = NULL; } @@ -1011,7 +1059,8 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, rfc1042 = hdr; rfc1042 += roundup(hdr_len, 4); - rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); + rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(ar, + enctype), 4); skb_pull(skb, sizeof(struct ethhdr)); memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)), @@ -1120,27 +1169,29 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt, bool channel_set, u32 attention) { + struct ath10k *ar = htt->ar; + if (head->len == 0) { - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx dropping due to zero-len\n"); return false; } if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) { - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx dropping due to decrypt-err\n"); return false; } if (!channel_set) { - ath10k_warn("no channel configured; ignoring frame!\n"); + ath10k_warn(ar, "no channel configured; ignoring frame!\n"); return false; } /* Skip mgmt frames while we handle this in WMI */ if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL || attention & RX_ATTENTION_FLAGS_MGMT_TYPE) { - ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n"); + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n"); return false; } @@ -1148,14 +1199,14 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt, status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR && status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER && !htt->ar->monitor_started) { - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx ignoring frame w/ status %d\n", status); return false; } if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) { - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx CAC running\n"); return false; } @@ -1166,6 +1217,7 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt, static void ath10k_htt_rx_handler(struct ath10k_htt *htt, struct htt_rx_indication *rx) { + struct ath10k *ar = htt->ar; struct ieee80211_rx_status *rx_status = &htt->rx_status; struct htt_rx_indication_mpdu_range *mpdu_ranges; struct htt_rx_desc *rxd; @@ -1211,7 +1263,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, rx_status); } - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", rx, sizeof(*rx) + (sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges)); @@ -1233,7 +1285,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, &attention); if (ret < 0) { - ath10k_warn("failed to pop amsdu from htt rx ring %d\n", + ath10k_warn(ar, "failed to pop amsdu from htt rx ring %d\n", ret); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; @@ -1282,6 +1334,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, struct htt_rx_fragment_indication *frag) { + struct ath10k *ar = htt->ar; struct sk_buff *msdu_head, *msdu_tail; enum htt_rx_mpdu_encrypt_type enctype; struct htt_rx_desc *rxd; @@ -1308,10 +1361,10 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, &attention); spin_unlock_bh(&htt->rx_ring.lock); - ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); + ath10k_dbg(ar, ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); if (ret) { - ath10k_warn("failed to pop amsdu from httr rx ring for fragmented rx %d\n", + ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n", ret); ath10k_htt_rx_free_msdu_chain(msdu_head); return; @@ -1328,7 +1381,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, RX_MSDU_START_INFO1_DECAP_FORMAT); if (fmt != RX_MSDU_DECAP_RAW) { - ath10k_warn("we dont support non-raw fragmented rx yet\n"); + ath10k_warn(ar, "we dont support non-raw fragmented rx yet\n"); dev_kfree_skb_any(msdu_head); goto end; } @@ -1340,17 +1393,17 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); if (tkip_mic_err) - ath10k_warn("tkip mic error\n"); + ath10k_warn(ar, "tkip mic error\n"); if (decrypt_err) { - ath10k_warn("decryption err in fragmented rx\n"); + ath10k_warn(ar, "decryption err in fragmented rx\n"); dev_kfree_skb_any(msdu_head); goto end; } if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) { hdrlen = ieee80211_hdrlen(hdr->frame_control); - paramlen = ath10k_htt_rx_crypto_param_len(enctype); + paramlen = ath10k_htt_rx_crypto_param_len(ar, enctype); /* It is more efficient to move the header than the payload */ memmove((void *)msdu_head->data + paramlen, @@ -1364,7 +1417,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, trim = 4; /* remove crypto trailer */ - trim += ath10k_htt_rx_crypto_tail_len(enctype); + trim += ath10k_htt_rx_crypto_tail_len(ar, enctype); /* last fragment of TKIP frags has MIC */ if (!ieee80211_has_morefrags(hdr->frame_control) && @@ -1372,20 +1425,20 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, trim += 8; if (trim > msdu_head->len) { - ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n"); + ath10k_warn(ar, "htt rx fragment: trailer longer than the frame itself? drop\n"); dev_kfree_skb_any(msdu_head); goto end; } skb_trim(msdu_head, msdu_head->len - trim); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ", + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ", msdu_head->data, msdu_head->len); ath10k_process_rx(htt->ar, rx_status, msdu_head); end: if (fw_desc_len > 0) { - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "expecting more fragmented rx in one indication %d\n", fw_desc_len); } @@ -1415,12 +1468,12 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar, tx_done.discard = true; break; default: - ath10k_warn("unhandled tx completion status %d\n", status); + ath10k_warn(ar, "unhandled tx completion status %d\n", status); tx_done.discard = true; break; } - ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n", resp->data_tx_completion.num_msdus); for (i = 0; i < resp->data_tx_completion.num_msdus; i++) { @@ -1441,14 +1494,14 @@ static void ath10k_htt_rx_addba(struct ath10k *ar, struct htt_resp *resp) tid = MS(info0, HTT_RX_BA_INFO0_TID); peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID); - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx addba tid %hu peer_id %hu size %hhu\n", tid, peer_id, ev->window_size); spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find_by_id(ar, peer_id); if (!peer) { - ath10k_warn("received addba event for invalid peer_id: %hu\n", + ath10k_warn(ar, "received addba event for invalid peer_id: %hu\n", peer_id); spin_unlock_bh(&ar->data_lock); return; @@ -1456,13 +1509,13 @@ static void ath10k_htt_rx_addba(struct ath10k *ar, struct htt_resp *resp) arvif = ath10k_get_arvif(ar, peer->vdev_id); if (!arvif) { - ath10k_warn("received addba event for invalid vdev_id: %u\n", + ath10k_warn(ar, "received addba event for invalid vdev_id: %u\n", peer->vdev_id); spin_unlock_bh(&ar->data_lock); return; } - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx start rx ba session sta %pM tid %hu size %hhu\n", peer->addr, tid, ev->window_size); @@ -1481,14 +1534,14 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) tid = MS(info0, HTT_RX_BA_INFO0_TID); peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID); - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx delba tid %hu peer_id %hu\n", tid, peer_id); spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find_by_id(ar, peer_id); if (!peer) { - ath10k_warn("received addba event for invalid peer_id: %hu\n", + ath10k_warn(ar, "received addba event for invalid peer_id: %hu\n", peer_id); spin_unlock_bh(&ar->data_lock); return; @@ -1496,13 +1549,13 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) arvif = ath10k_get_arvif(ar, peer->vdev_id); if (!arvif) { - ath10k_warn("received addba event for invalid vdev_id: %u\n", + ath10k_warn(ar, "received addba event for invalid vdev_id: %u\n", peer->vdev_id); spin_unlock_bh(&ar->data_lock); return; } - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx stop rx ba session sta %pM tid %hu\n", peer->addr, tid); @@ -1517,9 +1570,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) /* confirm alignment */ if (!IS_ALIGNED((unsigned long)skb->data, 4)) - ath10k_warn("unaligned htt message, expect trouble\n"); + ath10k_warn(ar, "unaligned htt message, expect trouble\n"); - ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n", resp->hdr.msg_type); switch (resp->hdr.msg_type) { case HTT_T2H_MSG_TYPE_VERSION_CONF: { @@ -1583,7 +1636,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) struct ath10k *ar = htt->ar; struct htt_security_indication *ev = &resp->security_indication; - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "sec ind peer_id %d unicast %d type %d\n", __le16_to_cpu(ev->peer_id), !!(ev->flags & HTT_SECURITY_IS_UNICAST), @@ -1592,7 +1645,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_FRAG_IND: { - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", skb->data, skb->len); ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind); break; @@ -1609,7 +1662,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) * sends all tx frames as already inspected so this shouldn't * happen unless fw has a bug. */ - ath10k_warn("received an unexpected htt tx inspect event\n"); + ath10k_warn(ar, "received an unexpected htt tx inspect event\n"); break; case HTT_T2H_MSG_TYPE_RX_ADDBA: ath10k_htt_rx_addba(ar, resp); @@ -1624,9 +1677,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } default: - ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt event (%d) not handled\n", resp->hdr.msg_type); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", skb->data, skb->len); break; }; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 8b27bfcc1de3..eaa73aa99c20 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -58,6 +58,7 @@ static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; int msdu_id; lockdep_assert_held(&htt->tx_lock); @@ -67,24 +68,29 @@ int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt) if (msdu_id == htt->max_num_pending_tx) return -ENOBUFS; - ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id); + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id); __set_bit(msdu_id, htt->used_msdu_ids); return msdu_id; } void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) { + struct ath10k *ar = htt->ar; + lockdep_assert_held(&htt->tx_lock); if (!test_bit(msdu_id, htt->used_msdu_ids)) - ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id); + ath10k_warn(ar, "trying to free unallocated msdu_id %d\n", + msdu_id); - ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id); + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id); __clear_bit(msdu_id, htt->used_msdu_ids); } int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; + spin_lock_init(&htt->tx_lock); init_waitqueue_head(&htt->empty_tx_wq); @@ -93,7 +99,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) else htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC; - ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", htt->max_num_pending_tx); htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) * @@ -122,6 +128,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; struct htt_tx_done tx_done = {0}; int msdu_id; @@ -130,7 +137,7 @@ static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt) if (!test_bit(msdu_id, htt->used_msdu_ids)) continue; - ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id); tx_done.discard = 1; @@ -157,6 +164,7 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; struct sk_buff *skb; struct htt_cmd *cmd; int len = 0; @@ -165,7 +173,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) len += sizeof(cmd->hdr); len += sizeof(cmd->ver_req); - skb = ath10k_htc_alloc_skb(len); + skb = ath10k_htc_alloc_skb(ar, len); if (!skb) return -ENOMEM; @@ -184,6 +192,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) { + struct ath10k *ar = htt->ar; struct htt_stats_req *req; struct sk_buff *skb; struct htt_cmd *cmd; @@ -192,7 +201,7 @@ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) len += sizeof(cmd->hdr); len += sizeof(cmd->stats_req); - skb = ath10k_htc_alloc_skb(len); + skb = ath10k_htc_alloc_skb(ar, len); if (!skb) return -ENOMEM; @@ -214,7 +223,8 @@ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { - ath10k_warn("failed to send htt type stats request: %d", ret); + ath10k_warn(ar, "failed to send htt type stats request: %d", + ret); dev_kfree_skb_any(skb); return ret; } @@ -224,6 +234,7 @@ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) { + struct ath10k *ar = htt->ar; struct sk_buff *skb; struct htt_cmd *cmd; struct htt_rx_ring_setup_ring *ring; @@ -242,7 +253,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr) + (sizeof(*ring) * num_rx_ring); - skb = ath10k_htc_alloc_skb(len); + skb = ath10k_htc_alloc_skb(ar, len); if (!skb) return -ENOMEM; @@ -311,6 +322,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu) { + struct ath10k *ar = htt->ar; struct htt_aggr_conf *aggr_conf; struct sk_buff *skb; struct htt_cmd *cmd; @@ -328,7 +340,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, len = sizeof(cmd->hdr); len += sizeof(cmd->aggr_conf); - skb = ath10k_htc_alloc_skb(len); + skb = ath10k_htc_alloc_skb(ar, len); if (!skb) return -ENOMEM; @@ -340,7 +352,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, aggr_conf->max_num_ampdu_subframes = max_subfrms_ampdu; aggr_conf->max_num_amsdu_subframes = max_subfrms_amsdu; - ath10k_dbg(ATH10K_DBG_HTT, "htt h2t aggr cfg msg amsdu %d ampdu %d", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt h2t aggr cfg msg amsdu %d ampdu %d", aggr_conf->max_num_amsdu_subframes, aggr_conf->max_num_ampdu_subframes); @@ -355,7 +367,8 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { - struct device *dev = htt->ar->dev; + struct ath10k *ar = htt->ar; + struct device *dev = ar->dev; struct sk_buff *txdesc = NULL; struct htt_cmd *cmd; struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); @@ -382,7 +395,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) htt->pending_tx[msdu_id] = msdu; spin_unlock_bh(&htt->tx_lock); - txdesc = ath10k_htc_alloc_skb(len); + txdesc = ath10k_htc_alloc_skb(ar, len); if (!txdesc) { res = -ENOMEM; goto err_free_msdu_id; @@ -429,7 +442,8 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { - struct device *dev = htt->ar->dev; + struct ath10k *ar = htt->ar; + struct device *dev = ar->dev; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); struct ath10k_hif_sg_item sg_items[2]; @@ -545,11 +559,11 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr); skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); - ath10k_dbg(ATH10K_DBG_HTT, + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n", flags0, flags1, msdu->len, msdu_id, frags_paddr, (u32)skb_cb->paddr, vdev_id, tid); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ", + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ", msdu->data, msdu->len); sg_items[0].transfer_id = 0; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 007e855f4ba9..13568b01de9f 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -28,16 +28,19 @@ #define QCA988X_HW_2_0_CHIP_ID_REV 0x2 #define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0" #define QCA988X_HW_2_0_FW_FILE "firmware.bin" -#define QCA988X_HW_2_0_FW_2_FILE "firmware-2.bin" +#define QCA988X_HW_2_0_FW_3_FILE "firmware-3.bin" #define QCA988X_HW_2_0_OTP_FILE "otp.bin" #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 #define ATH10K_FW_API2_FILE "firmware-2.bin" +#define ATH10K_FW_API3_FILE "firmware-3.bin" /* includes also the null byte */ #define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" +#define REG_DUMP_COUNT_QCA988X 60 + struct ath10k_fw_ie { __le32 id; __le32 len; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 9d61bb157189..b858c8288196 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -36,6 +36,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif, enum set_key_cmd cmd, const u8 *macaddr) { + struct ath10k *ar = arvif->ar; struct wmi_vdev_install_key_arg arg = { .vdev_id = arvif->vdev_id, .key_idx = key->keyidx, @@ -73,7 +74,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif, arg.key_flags = WMI_KEY_PAIRWISE; break; default: - ath10k_warn("cipher %d is not supported\n", key->cipher); + ath10k_warn(ar, "cipher %d is not supported\n", key->cipher); return -EOPNOTSUPP; } @@ -168,7 +169,7 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif, first_errno = ret; if (ret) - ath10k_warn("failed to remove peer wep key %d: %d\n", + ath10k_warn(ar, "failed to remove peer wep key %d: %d\n", i, ret); peer->keys[i] = NULL; @@ -216,7 +217,7 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif, first_errno = ret; if (ret) - ath10k_warn("failed to remove key for %pM: %d\n", + ath10k_warn(ar, "failed to remove key for %pM: %d\n", addr, ret); } @@ -327,14 +328,14 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) ret = ath10k_wmi_peer_create(ar, vdev_id, addr); if (ret) { - ath10k_warn("failed to create wmi peer %pM on vdev %i: %i\n", + ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n", addr, vdev_id, ret); return ret; } ret = ath10k_wait_for_peer_created(ar, vdev_id, addr); if (ret) { - ath10k_warn("failed to wait for created wmi peer %pM on vdev %i: %i\n", + ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n", addr, vdev_id, ret); return ret; } @@ -355,7 +356,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif) ret = ath10k_wmi_pdev_set_param(ar, param, ATH10K_KICKOUT_THRESHOLD); if (ret) { - ath10k_warn("failed to set kickout threshold on vdev %i: %d\n", + ath10k_warn(ar, "failed to set kickout threshold on vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -364,7 +365,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif) ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, ATH10K_KEEPALIVE_MIN_IDLE); if (ret) { - ath10k_warn("failed to set keepalive minimum idle time on vdev %i: %d\n", + ath10k_warn(ar, "failed to set keepalive minimum idle time on vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -373,7 +374,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif) ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, ATH10K_KEEPALIVE_MAX_IDLE); if (ret) { - ath10k_warn("failed to set keepalive maximum idle time on vdev %i: %d\n", + ath10k_warn(ar, "failed to set keepalive maximum idle time on vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -382,7 +383,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif) ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, ATH10K_KEEPALIVE_MAX_UNRESPONSIVE); if (ret) { - ath10k_warn("failed to set keepalive maximum unresponsive time on vdev %i: %d\n", + ath10k_warn(ar, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -449,7 +450,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) if (peer->vdev_id != vdev_id) continue; - ath10k_warn("removing stale peer %pM from vdev_id %d\n", + ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n", peer->addr, vdev_id); list_del(&peer->list); @@ -496,7 +497,7 @@ static bool ath10k_monitor_is_enabled(struct ath10k *ar) { lockdep_assert_held(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor refs: promisc %d monitor %d cac %d\n", ar->promisc, ar->monitor, test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)); @@ -531,35 +532,35 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { - ath10k_warn("failed to request monitor vdev %i start: %d\n", + ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n", vdev_id, ret); return ret; } ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn("failed to synchronize setup for monitor vdev %i: %d\n", + ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i: %d\n", vdev_id, ret); return ret; } ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); if (ret) { - ath10k_warn("failed to put up monitor vdev %i: %d\n", + ath10k_warn(ar, "failed to put up monitor vdev %i: %d\n", vdev_id, ret); goto vdev_stop; } ar->monitor_vdev_id = vdev_id; - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i started\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i started\n", ar->monitor_vdev_id); return 0; vdev_stop: ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("failed to stop monitor vdev %i after start failure: %d\n", + ath10k_warn(ar, "failed to stop monitor vdev %i after start failure: %d\n", ar->monitor_vdev_id, ret); return ret; @@ -573,20 +574,20 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("failed to put down monitor vdev %i: %d\n", + ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n", ar->monitor_vdev_id, ret); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("failed to to request monitor vdev %i stop: %d\n", + ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n", ar->monitor_vdev_id, ret); ret = ath10k_vdev_setup_sync(ar); if (ret) - ath10k_warn("failed to synchronise monitor vdev %i: %d\n", + ath10k_warn(ar, "failed to synchronise monitor vdev %i: %d\n", ar->monitor_vdev_id, ret); - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n", ar->monitor_vdev_id); return ret; } @@ -597,35 +598,29 @@ static int ath10k_monitor_vdev_create(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - bit = ffs(ar->free_vdev_map); - if (bit == 0) { - ath10k_warn("failed to find free vdev id for monitor vdev\n"); + if (ar->free_vdev_map == 0) { + ath10k_warn(ar, "failed to find free vdev id for monitor vdev\n"); return -ENOMEM; } + bit = ffs(ar->free_vdev_map); + ar->monitor_vdev_id = bit - 1; - ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id); ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id, WMI_VDEV_TYPE_MONITOR, 0, ar->mac_addr); if (ret) { - ath10k_warn("failed to request monitor vdev %i creation: %d\n", + ath10k_warn(ar, "failed to request monitor vdev %i creation: %d\n", ar->monitor_vdev_id, ret); - goto vdev_fail; + return ret; } - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n", + ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d created\n", ar->monitor_vdev_id); return 0; - -vdev_fail: - /* - * Restore the ID to the global map. - */ - ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); - return ret; } static int ath10k_monitor_vdev_delete(struct ath10k *ar) @@ -636,14 +631,14 @@ static int ath10k_monitor_vdev_delete(struct ath10k *ar) ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id); if (ret) { - ath10k_warn("failed to request wmi monitor vdev %i removal: %d\n", + ath10k_warn(ar, "failed to request wmi monitor vdev %i removal: %d\n", ar->monitor_vdev_id, ret); return ret; } - ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); + ar->free_vdev_map |= 1 << ar->monitor_vdev_id; - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n", ar->monitor_vdev_id); return ret; } @@ -655,30 +650,30 @@ static int ath10k_monitor_start(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); if (!ath10k_monitor_is_enabled(ar)) { - ath10k_warn("trying to start monitor with no references\n"); + ath10k_warn(ar, "trying to start monitor with no references\n"); return 0; } if (ar->monitor_started) { - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor already started\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor already started\n"); return 0; } ret = ath10k_monitor_vdev_create(ar); if (ret) { - ath10k_warn("failed to create monitor vdev: %d\n", ret); + ath10k_warn(ar, "failed to create monitor vdev: %d\n", ret); return ret; } ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id); if (ret) { - ath10k_warn("failed to start monitor vdev: %d\n", ret); + ath10k_warn(ar, "failed to start monitor vdev: %d\n", ret); ath10k_monitor_vdev_delete(ar); return ret; } ar->monitor_started = true; - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor started\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor started\n"); return 0; } @@ -690,27 +685,27 @@ static void ath10k_monitor_stop(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); if (ath10k_monitor_is_enabled(ar)) { - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor will be stopped later\n"); return; } if (!ar->monitor_started) { - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor probably failed to start earlier\n"); return; } ret = ath10k_monitor_vdev_stop(ar); if (ret) - ath10k_warn("failed to stop monitor vdev: %d\n", ret); + ath10k_warn(ar, "failed to stop monitor vdev: %d\n", ret); ret = ath10k_monitor_vdev_delete(ar); if (ret) - ath10k_warn("failed to delete monitor vdev: %d\n", ret); + ath10k_warn(ar, "failed to delete monitor vdev: %d\n", ret); ar->monitor_started = false; - ath10k_dbg(ATH10K_DBG_MAC, "mac monitor stopped\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor stopped\n"); } static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif) @@ -743,12 +738,12 @@ static int ath10k_start_cac(struct ath10k *ar) ret = ath10k_monitor_start(ar); if (ret) { - ath10k_warn("failed to start monitor (cac): %d\n", ret); + ath10k_warn(ar, "failed to start monitor (cac): %d\n", ret); clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); return ret; } - ath10k_dbg(ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n", ar->monitor_vdev_id); return 0; @@ -765,7 +760,7 @@ static int ath10k_stop_cac(struct ath10k *ar) clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); ath10k_monitor_stop(ar); - ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac cac finished\n"); return 0; } @@ -791,12 +786,12 @@ static void ath10k_recalc_radar_detection(struct ath10k *ar) * radiation is not allowed, make this channel DFS_UNAVAILABLE * by indicating that radar was detected. */ - ath10k_warn("failed to start CAC: %d\n", ret); + ath10k_warn(ar, "failed to start CAC: %d\n", ret); ieee80211_radar_detected(ar->hw); } } -static int ath10k_vdev_start(struct ath10k_vif *arvif) +static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, bool restart) { struct ath10k *ar = arvif->ar; struct cfg80211_chan_def *chandef = &ar->chandef; @@ -833,21 +828,25 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) arg.ssid_len = arvif->vif->bss_conf.ssid_len; } - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d start center_freq %d phymode %s\n", arg.vdev_id, arg.channel.freq, ath10k_wmi_phymode_str(arg.channel.mode)); - ret = ath10k_wmi_vdev_start(ar, &arg); + if (restart) + ret = ath10k_wmi_vdev_restart(ar, &arg); + else + ret = ath10k_wmi_vdev_start(ar, &arg); + if (ret) { - ath10k_warn("failed to start WMI vdev %i: %d\n", + ath10k_warn(ar, "failed to start WMI vdev %i: %d\n", arg.vdev_id, ret); return ret; } ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn("failed to synchronise setup for vdev %i: %d\n", + ath10k_warn(ar, "failed to synchronise setup for vdev %i: %d\n", arg.vdev_id, ret); return ret; } @@ -858,6 +857,16 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) return ret; } +static int ath10k_vdev_start(struct ath10k_vif *arvif) +{ + return ath10k_vdev_start_restart(arvif, false); +} + +static int ath10k_vdev_restart(struct ath10k_vif *arvif) +{ + return ath10k_vdev_start_restart(arvif, true); +} + static int ath10k_vdev_stop(struct ath10k_vif *arvif) { struct ath10k *ar = arvif->ar; @@ -869,14 +878,14 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); if (ret) { - ath10k_warn("failed to stop WMI vdev %i: %d\n", + ath10k_warn(ar, "failed to stop WMI vdev %i: %d\n", arvif->vdev_id, ret); return ret; } ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn("failed to syncronise setup for vdev %i: %d\n", + ath10k_warn(ar, "failed to syncronise setup for vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -894,6 +903,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) static void ath10k_control_beaconing(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info) { + struct ath10k *ar = arvif->ar; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -931,7 +941,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid); if (ret) { - ath10k_warn("failed to bring up vdev %d: %i\n", + ath10k_warn(ar, "failed to bring up vdev %d: %i\n", arvif->vdev_id, ret); ath10k_vdev_stop(arvif); return; @@ -940,13 +950,14 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, arvif->is_started = true; arvif->is_up = true; - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); } static void ath10k_control_ibss(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info, const u8 self_peer[ETH_ALEN]) { + struct ath10k *ar = arvif->ar; u32 vdev_param; int ret = 0; @@ -955,7 +966,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, if (!info->ibss_joined) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); if (ret) - ath10k_warn("failed to delete IBSS self peer %pM for vdev %d: %d\n", + ath10k_warn(ar, "failed to delete IBSS self peer %pM for vdev %d: %d\n", self_peer, arvif->vdev_id, ret); if (is_zero_ether_addr(arvif->bssid)) @@ -964,7 +975,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, arvif->bssid); if (ret) { - ath10k_warn("failed to delete IBSS BSSID peer %pM for vdev %d: %d\n", + ath10k_warn(ar, "failed to delete IBSS BSSID peer %pM for vdev %d: %d\n", arvif->bssid, arvif->vdev_id, ret); return; } @@ -976,7 +987,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer); if (ret) { - ath10k_warn("failed to create IBSS self peer %pM for vdev %d: %d\n", + ath10k_warn(ar, "failed to create IBSS self peer %pM for vdev %d: %d\n", self_peer, arvif->vdev_id, ret); return; } @@ -985,7 +996,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param, ATH10K_DEFAULT_ATIM); if (ret) - ath10k_warn("failed to set IBSS ATIM for vdev %d: %d\n", + ath10k_warn(ar, "failed to set IBSS ATIM for vdev %d: %d\n", arvif->vdev_id, ret); } @@ -1012,7 +1023,7 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, conf->dynamic_ps_timeout); if (ret) { - ath10k_warn("failed to set inactivity time for vdev %d: %i\n", + ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n", arvif->vdev_id, ret); return ret; } @@ -1020,12 +1031,12 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) psmode = WMI_STA_PS_MODE_DISABLED; } - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d psmode %s\n", arvif->vdev_id, psmode ? "enable" : "disable"); ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode); if (ret) { - ath10k_warn("failed to set PS Mode %d for vdev %d: %d\n", + ath10k_warn(ar, "failed to set PS Mode %d for vdev %d: %d\n", psmode, arvif->vdev_id, ret); return ret; } @@ -1109,12 +1120,12 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, /* FIXME: base on RSN IE/WPA IE is a correct idea? */ if (rsnie || wpaie) { - ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__); + ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__); arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; } if (wpaie) { - ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__); + ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__); arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; } } @@ -1223,7 +1234,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_num_spatial_streams = sta->rx_nss; } - ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", arg->addr, arg->peer_ht_rates.num_rates, arg->peer_num_spatial_streams); @@ -1240,7 +1251,7 @@ static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, lockdep_assert_held(&ar->conf_mutex); if (sta->wme && sta->uapsd_queues) { - ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", sta->uapsd_queues, sta->max_sp); if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) @@ -1265,7 +1276,7 @@ static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, WMI_AP_PS_PEER_PARAM_UAPSD, uapsd); if (ret) { - ath10k_warn("failed to set ap ps peer param uapsd for vdev %i: %d\n", + ath10k_warn(ar, "failed to set ap ps peer param uapsd for vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -1275,7 +1286,7 @@ static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, WMI_AP_PS_PEER_PARAM_MAX_SP, max_sp); if (ret) { - ath10k_warn("failed to set ap ps peer param max sp for vdev %i: %d\n", + ath10k_warn(ar, "failed to set ap ps peer param max sp for vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -1287,7 +1298,7 @@ static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr, WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10); if (ret) { - ath10k_warn("failed to set ap ps peer param ageout time for vdev %i: %d\n", + ath10k_warn(ar, "failed to set ap ps peer param ageout time for vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -1334,7 +1345,7 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, arg->peer_vht_rates.tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); - ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", sta->addr, arg->peer_max_mpdu, arg->peer_flags); } @@ -1407,7 +1418,7 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, break; } - ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM phymode %s\n", sta->addr, ath10k_wmi_phymode_str(phymode)); arg->peer_phymode = phymode; @@ -1480,7 +1491,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!ap_sta) { - ath10k_warn("failed to find station entry for bss %pM vdev %i\n", + ath10k_warn(ar, "failed to find station entry for bss %pM vdev %i\n", bss_conf->bssid, arvif->vdev_id); rcu_read_unlock(); return; @@ -1493,7 +1504,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta, bss_conf, &peer_arg); if (ret) { - ath10k_warn("failed to prepare peer assoc for %pM vdev %i: %d\n", + ath10k_warn(ar, "failed to prepare peer assoc for %pM vdev %i: %d\n", bss_conf->bssid, arvif->vdev_id, ret); rcu_read_unlock(); return; @@ -1503,19 +1514,19 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) { - ath10k_warn("failed to run peer assoc for %pM vdev %i: %d\n", + ath10k_warn(ar, "failed to run peer assoc for %pM vdev %i: %d\n", bss_conf->bssid, arvif->vdev_id, ret); return; } ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap); if (ret) { - ath10k_warn("failed to setup peer SMPS for vdev %i: %d\n", + ath10k_warn(ar, "failed to setup peer SMPS for vdev %i: %d\n", arvif->vdev_id, ret); return; } - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n", arvif->vdev_id, bss_conf->bssid, bss_conf->aid); @@ -1524,7 +1535,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); if (ret) { - ath10k_warn("failed to set vdev %d up: %d\n", + ath10k_warn(ar, "failed to set vdev %d up: %d\n", arvif->vdev_id, ret); return; } @@ -1550,7 +1561,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, * No idea why this happens, even though VDEV-DOWN is supposed * to be analogous to link down, so just stop the VDEV. */ - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n", arvif->vdev_id); /* FIXME: check return value */ @@ -1563,7 +1574,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, * interfaces as it expects there is no rx when no interface is * running. */ - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id); /* FIXME: why don't we print error if wmi call fails? */ ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); @@ -1584,7 +1595,7 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg); if (ret) { - ath10k_warn("failed to prepare WMI peer assoc for %pM vdev %i: %i\n", + ath10k_warn(ar, "failed to prepare WMI peer assoc for %pM vdev %i: %i\n", sta->addr, arvif->vdev_id, ret); return ret; } @@ -1592,14 +1603,14 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, peer_arg.peer_reassoc = reassoc; ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) { - ath10k_warn("failed to run peer assoc for STA %pM vdev %i: %d\n", + ath10k_warn(ar, "failed to run peer assoc for STA %pM vdev %i: %d\n", sta->addr, arvif->vdev_id, ret); return ret; } ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap); if (ret) { - ath10k_warn("failed to setup peer SMPS for vdev %d: %d\n", + ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n", arvif->vdev_id, ret); return ret; } @@ -1608,7 +1619,7 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, arvif->num_legacy_stations++; ret = ath10k_recalc_rtscts_prot(arvif); if (ret) { - ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n", + ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n", arvif->vdev_id, ret); return ret; } @@ -1616,14 +1627,14 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, ret = ath10k_install_peer_wep_keys(arvif, sta->addr); if (ret) { - ath10k_warn("failed to install peer wep keys for vdev %i: %d\n", + ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n", arvif->vdev_id, ret); return ret; } ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta); if (ret) { - ath10k_warn("failed to set qos params for STA %pM for vdev %i: %d\n", + ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n", sta->addr, arvif->vdev_id, ret); return ret; } @@ -1642,7 +1653,7 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, arvif->num_legacy_stations--; ret = ath10k_recalc_rtscts_prot(arvif); if (ret) { - ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n", + ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n", arvif->vdev_id, ret); return ret; } @@ -1650,7 +1661,7 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, ret = ath10k_clear_peer_keys(arvif, sta->addr); if (ret) { - ath10k_warn("failed to clear all peer wep keys for vdev %i: %d\n", + ath10k_warn(ar, "failed to clear all peer wep keys for vdev %i: %d\n", arvif->vdev_id, ret); return ret; } @@ -1742,7 +1753,7 @@ static int ath10k_update_channel_list(struct ath10k *ar) if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN)) continue; - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", ch - arg.channels, arg.n_channels, ch->freq, ch->max_power, ch->max_reg_power, @@ -1785,7 +1796,7 @@ static void ath10k_regd_update(struct ath10k *ar) ret = ath10k_update_channel_list(ar); if (ret) - ath10k_warn("failed to update channel list: %d\n", ret); + ath10k_warn(ar, "failed to update channel list: %d\n", ret); regpair = ar->ath_common.regulatory.regpair; @@ -1806,7 +1817,7 @@ static void ath10k_regd_update(struct ath10k *ar) regpair->reg_5ghz_ctl, wmi_dfs_reg); if (ret) - ath10k_warn("failed to set pdev regdomain: %d\n", ret); + ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret); } static void ath10k_reg_notifier(struct wiphy *wiphy, @@ -1819,12 +1830,12 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) { - ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs region 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs region 0x%x\n", request->dfs_region); result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector, request->dfs_region); if (!result) - ath10k_warn("DFS region 0x%X not supported, will trigger radar for every pulse\n", + ath10k_warn(ar, "DFS region 0x%X not supported, will trigger radar for every pulse\n", request->dfs_region); } @@ -1861,7 +1872,7 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, if (ar->monitor_started) return ar->monitor_vdev_id; - ath10k_warn("failed to resolve vdev id\n"); + ath10k_warn(ar, "failed to resolve vdev id\n"); return 0; } @@ -1897,6 +1908,7 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) { struct ath10k_vif *arvif = container_of(work, struct ath10k_vif, wep_key_work); + struct ath10k *ar = arvif->ar; int ret, keyidx = arvif->def_wep_key_newidx; mutex_lock(&arvif->ar->conf_mutex); @@ -1907,7 +1919,7 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) if (arvif->def_wep_key_idx == keyidx) goto unlock; - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", arvif->vdev_id, keyidx); ret = ath10k_wmi_vdev_set_param(arvif->ar, @@ -1915,7 +1927,7 @@ static void ath10k_tx_wep_key_work(struct work_struct *work) arvif->ar->wmi.vdev_param->def_keyid, keyidx); if (ret) { - ath10k_warn("failed to update wep key index for vdev %d: %d\n", + ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n", arvif->vdev_id, ret); goto unlock; @@ -1995,7 +2007,7 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) ar->fw_features)) { if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >= ATH10K_MAX_NUM_MGMT_PENDING) { - ath10k_warn("reached WMI management tranmist queue limit\n"); + ath10k_warn(ar, "reached WMI management transmit queue limit\n"); ret = -EBUSY; goto exit; } @@ -2019,7 +2031,8 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) exit: if (ret) { - ath10k_warn("failed to transmit packet, dropping: %d\n", ret); + ath10k_warn(ar, "failed to transmit packet, dropping: %d\n", + ret); ieee80211_free_txskb(ar->hw, skb); } } @@ -2061,7 +2074,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) mutex_lock(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %p\n", skb); hdr = (struct ieee80211_hdr *)skb->data; @@ -2074,13 +2087,13 @@ void ath10k_offchan_tx_work(struct work_struct *work) if (peer) /* FIXME: should this use ath10k_warn()? */ - ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n", peer_addr, vdev_id); if (!peer) { ret = ath10k_peer_create(ar, vdev_id, peer_addr); if (ret) - ath10k_warn("failed to create peer %pM on vdev %d: %d\n", + ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n", peer_addr, vdev_id, ret); } @@ -2094,13 +2107,13 @@ void ath10k_offchan_tx_work(struct work_struct *work) ret = wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ); if (ret <= 0) - ath10k_warn("timed out waiting for offchannel skb %p\n", + ath10k_warn(ar, "timed out waiting for offchannel skb %p\n", skb); if (!peer) { ret = ath10k_peer_delete(ar, vdev_id, peer_addr); if (ret) - ath10k_warn("failed to delete peer %pM on vdev %d: %d\n", + ath10k_warn(ar, "failed to delete peer %pM on vdev %d: %d\n", peer_addr, vdev_id, ret); } @@ -2134,7 +2147,7 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) ret = ath10k_wmi_mgmt_tx(ar, skb); if (ret) { - ath10k_warn("failed to transmit management frame via WMI: %d\n", + ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n", ret); ieee80211_free_txskb(ar->hw, skb); } @@ -2145,34 +2158,40 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) /* Scanning */ /************/ -/* - * This gets called if we dont get a heart-beat during scan. - * This may indicate the FW has hung and we need to abort the - * scan manually to prevent cancel_hw_scan() from deadlocking - */ -void ath10k_reset_scan(unsigned long ptr) +void __ath10k_scan_finish(struct ath10k *ar) { - struct ath10k *ar = (struct ath10k *)ptr; + lockdep_assert_held(&ar->data_lock); - spin_lock_bh(&ar->data_lock); - if (!ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); - return; + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + if (ar->scan.is_roc) + ieee80211_remain_on_channel_expired(ar->hw); + else + ieee80211_scan_completed(ar->hw, + (ar->scan.state == + ATH10K_SCAN_ABORTING)); + /* fall through */ + case ATH10K_SCAN_STARTING: + ar->scan.state = ATH10K_SCAN_IDLE; + ar->scan_channel = NULL; + ath10k_offchan_tx_purge(ar); + cancel_delayed_work(&ar->scan.timeout); + complete_all(&ar->scan.completed); + break; } +} - ath10k_warn("scan timed out, firmware problem?\n"); - - if (ar->scan.is_roc) - ieee80211_remain_on_channel_expired(ar->hw); - else - ieee80211_scan_completed(ar->hw, 1 /* aborted */); - - ar->scan.in_progress = false; - complete_all(&ar->scan.completed); +void ath10k_scan_finish(struct ath10k *ar) +{ + spin_lock_bh(&ar->data_lock); + __ath10k_scan_finish(ar); spin_unlock_bh(&ar->data_lock); } -static int ath10k_abort_scan(struct ath10k *ar) +static int ath10k_scan_stop(struct ath10k *ar) { struct wmi_stop_scan_arg arg = { .req_id = 1, /* FIXME */ @@ -2183,49 +2202,81 @@ static int ath10k_abort_scan(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - del_timer_sync(&ar->scan.timeout); - - spin_lock_bh(&ar->data_lock); - if (!ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); - return 0; - } - - ar->scan.aborting = true; - spin_unlock_bh(&ar->data_lock); - ret = ath10k_wmi_stop_scan(ar, &arg); if (ret) { - ath10k_warn("failed to stop wmi scan: %d\n", ret); - spin_lock_bh(&ar->data_lock); - ar->scan.in_progress = false; - ath10k_offchan_tx_purge(ar); - spin_unlock_bh(&ar->data_lock); - return -EIO; + ath10k_warn(ar, "failed to stop wmi scan: %d\n", ret); + goto out; } ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ); - if (ret == 0) - ath10k_warn("timed out while waiting for scan to stop\n"); - - /* scan completion may be done right after we timeout here, so let's - * check the in_progress and tell mac80211 scan is completed. if we - * don't do that and FW fails to send us scan completion indication - * then userspace won't be able to scan anymore */ - ret = 0; - - spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - ath10k_warn("failed to stop scan, it's still in progress\n"); - ar->scan.in_progress = false; - ath10k_offchan_tx_purge(ar); + if (ret == 0) { + ath10k_warn(ar, "failed to receive scan abortion completion: timed out\n"); ret = -ETIMEDOUT; + } else if (ret > 0) { + ret = 0; } + +out: + /* Scan state should be updated upon scan completion but in case + * firmware fails to deliver the event (for whatever reason) it is + * desired to clean up scan state anyway. Firmware may have just + * dropped the scan completion event delivery due to transport pipe + * being overflown with data and/or it can recover on its own before + * next scan request is submitted. + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state != ATH10K_SCAN_IDLE) + __ath10k_scan_finish(ar); spin_unlock_bh(&ar->data_lock); return ret; } +static void ath10k_scan_abort(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + /* This can happen if timeout worker kicked in and called + * abortion while scan completion was being processed. + */ + break; + case ATH10K_SCAN_STARTING: + case ATH10K_SCAN_ABORTING: + ath10k_warn(ar, "refusing scan abortion due to invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + ar->scan.state = ATH10K_SCAN_ABORTING; + spin_unlock_bh(&ar->data_lock); + + ret = ath10k_scan_stop(ar); + if (ret) + ath10k_warn(ar, "failed to abort scan: %d\n", ret); + + spin_lock_bh(&ar->data_lock); + break; + } + + spin_unlock_bh(&ar->data_lock); +} + +void ath10k_scan_timeout_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + scan.timeout.work); + + mutex_lock(&ar->conf_mutex); + ath10k_scan_abort(ar); + mutex_unlock(&ar->conf_mutex); +} + static int ath10k_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *arg) { @@ -2239,17 +2290,16 @@ static int ath10k_start_scan(struct ath10k *ar, ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ); if (ret == 0) { - ath10k_abort_scan(ar); - return ret; + ret = ath10k_scan_stop(ar); + if (ret) + ath10k_warn(ar, "failed to stop scan: %d\n", ret); + + return -ETIMEDOUT; } - /* the scan can complete earlier, before we even - * start the timer. in that case the timer handler - * checks ar->scan.in_progress and bails out if its - * false. Add a 200ms margin to account event/command - * processing. */ - mod_timer(&ar->scan.timeout, jiffies + - msecs_to_jiffies(arg->max_scan_time+200)); + /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, + msecs_to_jiffies(arg->max_scan_time+200)); return 0; } @@ -2269,7 +2319,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) - ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); ATH10K_SKB_CB(skb)->htt.is_offchan = false; ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); @@ -2289,7 +2339,8 @@ static void ath10k_tx(struct ieee80211_hw *hw, ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; spin_unlock_bh(&ar->data_lock); - ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); + ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n", + skb); skb_queue_tail(&ar->offchan_tx_queue, skb); ieee80211_queue_work(hw, &ar->offchan_tx_work); @@ -2325,8 +2376,7 @@ void ath10k_halt(struct ath10k *ar) ath10k_monitor_stop(ar); } - del_timer_sync(&ar->scan.timeout); - ath10k_reset_scan((unsigned long)ar); + ath10k_scan_finish(ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); @@ -2380,7 +2430,7 @@ static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask, tx_ant); if (ret) { - ath10k_warn("failed to set tx-chainmask: %d, req 0x%x\n", + ath10k_warn(ar, "failed to set tx-chainmask: %d, req 0x%x\n", ret, tx_ant); return ret; } @@ -2388,7 +2438,7 @@ static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask, rx_ant); if (ret) { - ath10k_warn("failed to set rx-chainmask: %d, req 0x%x\n", + ath10k_warn(ar, "failed to set rx-chainmask: %d, req 0x%x\n", ret, rx_ant); return ret; } @@ -2439,25 +2489,25 @@ static int ath10k_start(struct ieee80211_hw *hw) ret = ath10k_hif_power_up(ar); if (ret) { - ath10k_err("Could not init hif: %d\n", ret); + ath10k_err(ar, "Could not init hif: %d\n", ret); goto err_off; } ret = ath10k_core_start(ar); if (ret) { - ath10k_err("Could not init core: %d\n", ret); + ath10k_err(ar, "Could not init core: %d\n", ret); goto err_power_down; } ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); if (ret) { - ath10k_warn("failed to enable PMF QOS: %d\n", ret); + ath10k_warn(ar, "failed to enable PMF QOS: %d\n", ret); goto err_core_stop; } ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1); if (ret) { - ath10k_warn("failed to enable dynamic BW: %d\n", ret); + ath10k_warn(ar, "failed to enable dynamic BW: %d\n", ret); goto err_core_stop; } @@ -2477,7 +2527,7 @@ static int ath10k_start(struct ieee80211_hw *hw) ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->arp_ac_override, 0); if (ret) { - ath10k_warn("failed to set arp ac override parameter: %d\n", + ath10k_warn(ar, "failed to set arp ac override parameter: %d\n", ret); goto err_core_stop; } @@ -2485,6 +2535,8 @@ static int ath10k_start(struct ieee80211_hw *hw) ar->num_started_vdevs = 0; ath10k_regd_update(ar); + ath10k_spectral_start(ar); + mutex_unlock(&ar->conf_mutex); return 0; @@ -2515,6 +2567,7 @@ static void ath10k_stop(struct ieee80211_hw *hw) } mutex_unlock(&ar->conf_mutex); + cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->restart_work); } @@ -2528,7 +2581,7 @@ static int ath10k_config_ps(struct ath10k *ar) list_for_each_entry(arvif, &ar->arvifs, list) { ret = ath10k_mac_vif_setup_ps(arvif); if (ret) { - ath10k_warn("failed to setup powersave: %d\n", ret); + ath10k_warn(ar, "failed to setup powersave: %d\n", ret); break; } } @@ -2566,7 +2619,7 @@ static void ath10k_config_chan(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n", ar->chandef.chan->center_freq, ar->chandef.center_freq1, @@ -2582,18 +2635,21 @@ static void ath10k_config_chan(struct ath10k *ar) if (!arvif->is_started) continue; + if (!arvif->is_up) + continue; + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) continue; - ret = ath10k_vdev_stop(arvif); + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); if (ret) { - ath10k_warn("failed to stop vdev %d: %d\n", + ath10k_warn(ar, "failed to down vdev %d: %d\n", arvif->vdev_id, ret); continue; } } - /* all vdevs are now stopped - now attempt to restart them */ + /* all vdevs are downed now - attempt to restart and re-up them */ list_for_each_entry(arvif, &ar->arvifs, list) { if (!arvif->is_started) @@ -2602,9 +2658,9 @@ static void ath10k_config_chan(struct ath10k *ar) if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) continue; - ret = ath10k_vdev_start(arvif); + ret = ath10k_vdev_restart(arvif); if (ret) { - ath10k_warn("failed to start vdev %d: %d\n", + ath10k_warn(ar, "failed to restart vdev %d: %d\n", arvif->vdev_id, ret); continue; } @@ -2615,7 +2671,7 @@ static void ath10k_config_chan(struct ath10k *ar) ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid); if (ret) { - ath10k_warn("failed to bring vdev up %d: %d\n", + ath10k_warn(ar, "failed to bring vdev up %d: %d\n", arvif->vdev_id, ret); continue; } @@ -2635,7 +2691,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&ar->conf_mutex); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac config channel %dMHz flags 0x%x radar %d\n", conf->chandef.chan->center_freq, conf->chandef.chan->flags, @@ -2655,21 +2711,21 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_POWER) { - ath10k_dbg(ATH10K_DBG_MAC, "mac config power %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac config power %d\n", hw->conf.power_level); param = ar->wmi.pdev_param->txpower_limit2g; ret = ath10k_wmi_pdev_set_param(ar, param, hw->conf.power_level * 2); if (ret) - ath10k_warn("failed to set 2g txpower %d: %d\n", + ath10k_warn(ar, "failed to set 2g txpower %d: %d\n", hw->conf.power_level, ret); param = ar->wmi.pdev_param->txpower_limit5g; ret = ath10k_wmi_pdev_set_param(ar, param, hw->conf.power_level * 2); if (ret) - ath10k_warn("failed to set 5g txpower %d: %d\n", + ath10k_warn(ar, "failed to set 5g txpower %d: %d\n", hw->conf.power_level, ret); } @@ -2681,7 +2737,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ar->monitor = true; ret = ath10k_monitor_start(ar); if (ret) { - ath10k_warn("failed to start monitor (config): %d\n", + ath10k_warn(ar, "failed to start monitor (config): %d\n", ret); ar->monitor = false; } @@ -2724,11 +2780,12 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work); INIT_LIST_HEAD(&arvif->list); - bit = ffs(ar->free_vdev_map); - if (bit == 0) { + if (ar->free_vdev_map == 0) { + ath10k_warn(ar, "Free vdev map is empty, no more interfaces allowed.\n"); ret = -EBUSY; goto err; } + bit = ffs(ar->free_vdev_map); arvif->vdev_id = bit - 1; arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; @@ -2760,25 +2817,25 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, break; } - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n", arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype); ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, vif->addr); if (ret) { - ath10k_warn("failed to create WMI vdev %i: %d\n", + ath10k_warn(ar, "failed to create WMI vdev %i: %d\n", arvif->vdev_id, ret); goto err; } - ar->free_vdev_map &= ~BIT(arvif->vdev_id); + ar->free_vdev_map &= ~(1 << arvif->vdev_id); list_add(&arvif->list, &ar->arvifs); vdev_param = ar->wmi.vdev_param->def_keyid; ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, arvif->def_wep_key_idx); if (ret) { - ath10k_warn("failed to set vdev %i default key id: %d\n", + ath10k_warn(ar, "failed to set vdev %i default key id: %d\n", arvif->vdev_id, ret); goto err_vdev_delete; } @@ -2788,7 +2845,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ATH10K_HW_TXRX_NATIVE_WIFI); /* 10.X firmware does not support this VDEV parameter. Do not warn */ if (ret && ret != -EOPNOTSUPP) { - ath10k_warn("failed to set vdev %i TX encapsulation: %d\n", + ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n", arvif->vdev_id, ret); goto err_vdev_delete; } @@ -2796,14 +2853,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr); if (ret) { - ath10k_warn("failed to create vdev %i peer for AP: %d\n", + ath10k_warn(ar, "failed to create vdev %i peer for AP: %d\n", arvif->vdev_id, ret); goto err_vdev_delete; } ret = ath10k_mac_set_kickout(arvif); if (ret) { - ath10k_warn("failed to set vdev %i kickout parameters: %d\n", + ath10k_warn(ar, "failed to set vdev %i kickout parameters: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -2815,7 +2872,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); if (ret) { - ath10k_warn("failed to set vdev %i RX wake policy: %d\n", + ath10k_warn(ar, "failed to set vdev %i RX wake policy: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -2825,7 +2882,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); if (ret) { - ath10k_warn("failed to set vdev %i TX wake thresh: %d\n", + ath10k_warn(ar, "failed to set vdev %i TX wake thresh: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -2835,7 +2892,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); if (ret) { - ath10k_warn("failed to set vdev %i PSPOLL count: %d\n", + ath10k_warn(ar, "failed to set vdev %i PSPOLL count: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -2843,14 +2900,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); if (ret) { - ath10k_warn("failed to set rts threshold for vdev %d: %d\n", + ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold); if (ret) { - ath10k_warn("failed to set frag threshold for vdev %d: %d\n", + ath10k_warn(ar, "failed to set frag threshold for vdev %d: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -2864,7 +2921,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, err_vdev_delete: ath10k_wmi_vdev_delete(ar, arvif->vdev_id); - ar->free_vdev_map &= ~BIT(arvif->vdev_id); + ar->free_vdev_map |= 1 << arvif->vdev_id; list_del(&arvif->list); err: @@ -2892,26 +2949,32 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, dev_kfree_skb_any(arvif->beacon); arvif->beacon = NULL; } + spin_unlock_bh(&ar->data_lock); - ar->free_vdev_map |= 1 << (arvif->vdev_id); + ret = ath10k_spectral_vif_stop(arvif); + if (ret) + ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n", + arvif->vdev_id, ret); + + ar->free_vdev_map |= 1 << arvif->vdev_id; list_del(&arvif->list); if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); if (ret) - ath10k_warn("failed to remove peer for AP vdev %i: %d\n", + ath10k_warn(ar, "failed to remove peer for AP vdev %i: %d\n", arvif->vdev_id, ret); kfree(arvif->u.ap.noa_data); } - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n", arvif->vdev_id); ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id); if (ret) - ath10k_warn("failed to delete WMI vdev %i: %d\n", + ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n", arvif->vdev_id, ret); ath10k_peer_cleanup(ar, arvif->vdev_id); @@ -2950,7 +3013,7 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw, ar->promisc = true; ret = ath10k_monitor_start(ar); if (ret) { - ath10k_warn("failed to start monitor (promisc): %d\n", + ath10k_warn(ar, "failed to start monitor (promisc): %d\n", ret); ar->promisc = false; } @@ -2982,17 +3045,17 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, vdev_param = ar->wmi.vdev_param->beacon_interval; ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->beacon_interval); - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d beacon_interval %d\n", arvif->vdev_id, arvif->beacon_interval); if (ret) - ath10k_warn("failed to set beacon interval for vdev %d: %i\n", + ath10k_warn(ar, "failed to set beacon interval for vdev %d: %i\n", arvif->vdev_id, ret); } if (changed & BSS_CHANGED_BEACON) { - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "vdev %d set beacon tx mode to staggered\n", arvif->vdev_id); @@ -3000,14 +3063,14 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ret = ath10k_wmi_pdev_set_param(ar, pdev_param, WMI_BEACON_STAGGERED_MODE); if (ret) - ath10k_warn("failed to set beacon mode for vdev %d: %i\n", + ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n", arvif->vdev_id, ret); } if (changed & BSS_CHANGED_BEACON_INFO) { arvif->dtim_period = info->dtim_period; - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d dtim_period %d\n", arvif->vdev_id, arvif->dtim_period); @@ -3015,7 +3078,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->dtim_period); if (ret) - ath10k_warn("failed to set dtim period for vdev %d: %i\n", + ath10k_warn(ar, "failed to set dtim period for vdev %d: %i\n", arvif->vdev_id, ret); } @@ -3034,14 +3097,14 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID && vif->type != NL80211_IFTYPE_AP) { if (!is_zero_ether_addr(info->bssid)) { - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d create peer %pM\n", arvif->vdev_id, info->bssid); ret = ath10k_peer_create(ar, arvif->vdev_id, info->bssid); if (ret) - ath10k_warn("failed to add peer %pM for vdev %d when changing bssid: %i\n", + ath10k_warn(ar, "failed to add peer %pM for vdev %d when changing bssid: %i\n", info->bssid, arvif->vdev_id, ret); if (vif->type == NL80211_IFTYPE_STATION) { @@ -3051,13 +3114,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, */ memcpy(arvif->bssid, info->bssid, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d start %pM\n", arvif->vdev_id, info->bssid); ret = ath10k_vdev_start(arvif); if (ret) { - ath10k_warn("failed to start vdev %i: %d\n", + ath10k_warn(ar, "failed to start vdev %i: %d\n", arvif->vdev_id, ret); goto exit; } @@ -3081,12 +3144,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ERP_CTS_PROT) { arvif->use_cts_prot = info->use_cts_prot; - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n", arvif->vdev_id, info->use_cts_prot); ret = ath10k_recalc_rtscts_prot(arvif); if (ret) - ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n", + ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n", arvif->vdev_id, ret); } @@ -3098,14 +3161,14 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n", arvif->vdev_id, slottime); vdev_param = ar->wmi.vdev_param->slot_time; ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, slottime); if (ret) - ath10k_warn("failed to set erp slot for vdev %d: %i\n", + ath10k_warn(ar, "failed to set erp slot for vdev %d: %i\n", arvif->vdev_id, ret); } @@ -3116,7 +3179,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else preamble = WMI_VDEV_PREAMBLE_LONG; - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d preamble %dn", arvif->vdev_id, preamble); @@ -3124,7 +3187,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, preamble); if (ret) - ath10k_warn("failed to set preamble for vdev %d: %i\n", + ath10k_warn(ar, "failed to set preamble for vdev %d: %i\n", arvif->vdev_id, ret); } @@ -3151,20 +3214,26 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + ar->scan.state = ATH10K_SCAN_STARTING; + ar->scan.is_roc = false; + ar->scan.vdev_id = arvif->vdev_id; + ret = 0; + break; + case ATH10K_SCAN_STARTING: + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: ret = -EBUSY; - goto exit; + break; } - - reinit_completion(&ar->scan.started); - reinit_completion(&ar->scan.completed); - ar->scan.in_progress = true; - ar->scan.aborting = false; - ar->scan.is_roc = false; - ar->scan.vdev_id = arvif->vdev_id; spin_unlock_bh(&ar->data_lock); + if (ret) + goto exit; + memset(&arg, 0, sizeof(arg)); ath10k_wmi_start_scan_init(ar, &arg); arg.vdev_id = arvif->vdev_id; @@ -3196,9 +3265,9 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, ret = ath10k_start_scan(ar, &arg); if (ret) { - ath10k_warn("failed to start hw scan: %d\n", ret); + ath10k_warn(ar, "failed to start hw scan: %d\n", ret); spin_lock_bh(&ar->data_lock); - ar->scan.in_progress = false; + ar->scan.state = ATH10K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); } @@ -3211,14 +3280,10 @@ static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath10k *ar = hw->priv; - int ret; mutex_lock(&ar->conf_mutex); - ret = ath10k_abort_scan(ar); - if (ret) { - ath10k_warn("failed to abort scan: %d\n", ret); - ieee80211_scan_completed(hw, 1 /* aborted */); - } + cancel_delayed_work_sync(&ar->scan.timeout); + ath10k_scan_abort(ar); mutex_unlock(&ar->conf_mutex); } @@ -3256,7 +3321,7 @@ static void ath10k_set_key_h_def_keyidx(struct ath10k *ar, ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, key->keyidx); if (ret) - ath10k_warn("failed to set vdev %i group key as default key: %d\n", + ath10k_warn(ar, "failed to set vdev %i group key as default key: %d\n", arvif->vdev_id, ret); } @@ -3294,7 +3359,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (!peer) { if (cmd == SET_KEY) { - ath10k_warn("failed to install key for non-existent peer %pM\n", + ath10k_warn(ar, "failed to install key for non-existent peer %pM\n", peer_addr); ret = -EOPNOTSUPP; goto exit; @@ -3317,7 +3382,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = ath10k_install_key(arvif, key, cmd, peer_addr); if (ret) { - ath10k_warn("failed to install key for vdev %i peer %pM: %d\n", + ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n", arvif->vdev_id, peer_addr, ret); goto exit; } @@ -3332,7 +3397,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, peer->keys[key->keyidx] = NULL; else if (peer == NULL) /* impossible unless FW goes crazy */ - ath10k_warn("Peer %pM disappeared!\n", peer_addr); + ath10k_warn(ar, "Peer %pM disappeared!\n", peer_addr); spin_unlock_bh(&ar->data_lock); exit: @@ -3368,45 +3433,45 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk) mutex_lock(&ar->conf_mutex); if (changed & IEEE80211_RC_BW_CHANGED) { - ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n", sta->addr, bw); err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, WMI_PEER_CHAN_WIDTH, bw); if (err) - ath10k_warn("failed to update STA %pM peer bw %d: %d\n", + ath10k_warn(ar, "failed to update STA %pM peer bw %d: %d\n", sta->addr, bw, err); } if (changed & IEEE80211_RC_NSS_CHANGED) { - ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM nss %d\n", sta->addr, nss); err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, WMI_PEER_NSS, nss); if (err) - ath10k_warn("failed to update STA %pM nss %d: %d\n", + ath10k_warn(ar, "failed to update STA %pM nss %d: %d\n", sta->addr, nss, err); } if (changed & IEEE80211_RC_SMPS_CHANGED) { - ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM smps %d\n", sta->addr, smps); err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, WMI_PEER_SMPS_STATE, smps); if (err) - ath10k_warn("failed to update STA %pM smps %d: %d\n", + ath10k_warn(ar, "failed to update STA %pM smps %d: %d\n", sta->addr, smps, err); } if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { - ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM supp rates\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n", sta->addr); err = ath10k_station_assoc(ar, arvif, sta, true); if (err) - ath10k_warn("failed to reassociate station: %pM\n", + ath10k_warn(ar, "failed to reassociate station: %pM\n", sta->addr); } @@ -3451,31 +3516,31 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, max_num_peers = TARGET_NUM_PEERS; if (ar->num_peers >= max_num_peers) { - ath10k_warn("number of peers exceeded: peers number %d (max peers %d)\n", + ath10k_warn(ar, "number of peers exceeded: peers number %d (max peers %d)\n", ar->num_peers, max_num_peers); ret = -ENOBUFS; goto exit; } - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d peer create %pM (new sta) num_peers %d\n", arvif->vdev_id, sta->addr, ar->num_peers); ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); if (ret) - ath10k_warn("failed to add peer %pM for vdev %d when adding a new sta: %i\n", + ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n", sta->addr, arvif->vdev_id, ret); } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { /* * Existing station deletion. */ - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d peer delete %pM (sta gone)\n", arvif->vdev_id, sta->addr); ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr); if (ret) - ath10k_warn("failed to delete peer %pM for vdev %d: %i\n", + ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n", sta->addr, arvif->vdev_id, ret); if (vif->type == NL80211_IFTYPE_STATION) @@ -3487,12 +3552,12 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New association. */ - ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM associated\n", sta->addr); ret = ath10k_station_assoc(ar, arvif, sta, false); if (ret) - ath10k_warn("failed to associate station %pM for vdev %i: %i\n", + ath10k_warn(ar, "failed to associate station %pM for vdev %i: %i\n", sta->addr, arvif->vdev_id, ret); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && @@ -3501,12 +3566,12 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * Disassociation. */ - ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM disassociated\n", sta->addr); ret = ath10k_station_disassoc(ar, arvif, sta); if (ret) - ath10k_warn("failed to disassociate station: %pM vdev %i: %i\n", + ath10k_warn(ar, "failed to disassociate station: %pM vdev %i: %i\n", sta->addr, arvif->vdev_id, ret); } exit: @@ -3554,7 +3619,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, WMI_STA_PS_PARAM_UAPSD, arvif->u.sta.uapsd); if (ret) { - ath10k_warn("failed to set uapsd params: %d\n", ret); + ath10k_warn(ar, "failed to set uapsd params: %d\n", ret); goto exit; } @@ -3567,7 +3632,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, WMI_STA_PS_PARAM_RX_WAKE_POLICY, value); if (ret) - ath10k_warn("failed to set rx wake param: %d\n", ret); + ath10k_warn(ar, "failed to set rx wake param: %d\n", ret); exit: return ret; @@ -3617,13 +3682,13 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw, /* FIXME: FW accepts wmm params per hw, not per vif */ ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params); if (ret) { - ath10k_warn("failed to set wmm params: %d\n", ret); + ath10k_warn(ar, "failed to set wmm params: %d\n", ret); goto exit; } ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd); if (ret) - ath10k_warn("failed to set sta uapsd: %d\n", ret); + ath10k_warn(ar, "failed to set sta uapsd: %d\n", ret); exit: mutex_unlock(&ar->conf_mutex); @@ -3641,27 +3706,33 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct wmi_start_scan_arg arg; - int ret; + int ret = 0; mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - spin_unlock_bh(&ar->data_lock); + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + reinit_completion(&ar->scan.on_channel); + ar->scan.state = ATH10K_SCAN_STARTING; + ar->scan.is_roc = true; + ar->scan.vdev_id = arvif->vdev_id; + ar->scan.roc_freq = chan->center_freq; + ret = 0; + break; + case ATH10K_SCAN_STARTING: + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: ret = -EBUSY; - goto exit; + break; } - - reinit_completion(&ar->scan.started); - reinit_completion(&ar->scan.completed); - reinit_completion(&ar->scan.on_channel); - ar->scan.in_progress = true; - ar->scan.aborting = false; - ar->scan.is_roc = true; - ar->scan.vdev_id = arvif->vdev_id; - ar->scan.roc_freq = chan->center_freq; spin_unlock_bh(&ar->data_lock); + if (ret) + goto exit; + memset(&arg, 0, sizeof(arg)); ath10k_wmi_start_scan_init(ar, &arg); arg.vdev_id = arvif->vdev_id; @@ -3676,17 +3747,21 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, ret = ath10k_start_scan(ar, &arg); if (ret) { - ath10k_warn("failed to start roc scan: %d\n", ret); + ath10k_warn(ar, "failed to start roc scan: %d\n", ret); spin_lock_bh(&ar->data_lock); - ar->scan.in_progress = false; + ar->scan.state = ATH10K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); goto exit; } ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ); if (ret == 0) { - ath10k_warn("failed to switch to channel for roc scan\n"); - ath10k_abort_scan(ar); + ath10k_warn(ar, "failed to switch to channel for roc scan\n"); + + ret = ath10k_scan_stop(ar); + if (ret) + ath10k_warn(ar, "failed to stop scan: %d\n", ret); + ret = -ETIMEDOUT; goto exit; } @@ -3702,7 +3777,8 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; mutex_lock(&ar->conf_mutex); - ath10k_abort_scan(ar); + cancel_delayed_work_sync(&ar->scan.timeout); + ath10k_scan_abort(ar); mutex_unlock(&ar->conf_mutex); return 0; @@ -3721,12 +3797,12 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) mutex_lock(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n", arvif->vdev_id, value); ret = ath10k_mac_set_rts(arvif, value); if (ret) { - ath10k_warn("failed to set rts threshold for vdev %d: %d\n", + ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n", arvif->vdev_id, ret); break; } @@ -3744,12 +3820,12 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) mutex_lock(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", arvif->vdev_id, value); ret = ath10k_mac_set_rts(arvif, value); if (ret) { - ath10k_warn("failed to set fragmentation threshold for vdev %d: %d\n", + ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n", arvif->vdev_id, ret); break; } @@ -3789,7 +3865,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, }), ATH10K_FLUSH_TIMEOUT_HZ); if (ret <= 0 || skip) - ath10k_warn("failed to flush transmit queue (skip %i ar-state %i): %i\n", + ath10k_warn(ar, "failed to flush transmit queue (skip %i ar-state %i): %i\n", skip, ar->state, ret); skip: @@ -3824,7 +3900,7 @@ static int ath10k_suspend(struct ieee80211_hw *hw, ret = ath10k_hif_suspend(ar); if (ret) { - ath10k_warn("failed to suspend hif: %d\n", ret); + ath10k_warn(ar, "failed to suspend hif: %d\n", ret); goto resume; } @@ -3833,7 +3909,7 @@ static int ath10k_suspend(struct ieee80211_hw *hw, resume: ret = ath10k_wmi_pdev_resume_target(ar); if (ret) - ath10k_warn("failed to resume target: %d\n", ret); + ath10k_warn(ar, "failed to resume target: %d\n", ret); ret = 1; exit: @@ -3850,14 +3926,14 @@ static int ath10k_resume(struct ieee80211_hw *hw) ret = ath10k_hif_resume(ar); if (ret) { - ath10k_warn("failed to resume hif: %d\n", ret); + ath10k_warn(ar, "failed to resume hif: %d\n", ret); ret = 1; goto exit; } ret = ath10k_wmi_pdev_resume_target(ar); if (ret) { - ath10k_warn("failed to resume target: %d\n", ret); + ath10k_warn(ar, "failed to resume target: %d\n", ret); ret = 1; goto exit; } @@ -3878,7 +3954,7 @@ static void ath10k_restart_complete(struct ieee80211_hw *hw) /* If device failed to restart it will be in a different state, e.g. * ATH10K_STATE_WEDGED */ if (ar->state == ATH10K_STATE_RESTARTED) { - ath10k_info("device successfully recovered\n"); + ath10k_info(ar, "device successfully recovered\n"); ar->state = ATH10K_STATE_ON; } @@ -4075,7 +4151,8 @@ ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask, } static bool -ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask, +ath10k_bitrate_mask_rate(struct ath10k *ar, + const struct cfg80211_bitrate_mask *mask, enum ieee80211_band band, u8 *fixed_rate, u8 *fixed_nss) @@ -4133,7 +4210,7 @@ ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask, nss <<= 4; pream <<= 6; - ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n", pream, nss, rate); *fixed_rate = pream | nss | rate; @@ -4141,7 +4218,8 @@ ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask, return true; } -static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask, +static bool ath10k_get_fixed_rate_nss(struct ath10k *ar, + const struct cfg80211_bitrate_mask *mask, enum ieee80211_band band, u8 *fixed_rate, u8 *fixed_nss) @@ -4151,7 +4229,7 @@ static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask, return true; /* Next Check single rate is set */ - return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss); + return ath10k_bitrate_mask_rate(ar, mask, band, fixed_rate, fixed_nss); } static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, @@ -4171,16 +4249,16 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, goto exit; if (fixed_rate == WMI_FIXED_RATE_NONE) - ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n"); if (force_sgi) - ath10k_dbg(ATH10K_DBG_MAC, "mac force sgi\n"); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac force sgi\n"); vdev_param = ar->wmi.vdev_param->fixed_rate; ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, fixed_rate); if (ret) { - ath10k_warn("failed to set fixed rate param 0x%02x: %d\n", + ath10k_warn(ar, "failed to set fixed rate param 0x%02x: %d\n", fixed_rate, ret); ret = -EINVAL; goto exit; @@ -4193,7 +4271,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, vdev_param, fixed_nss); if (ret) { - ath10k_warn("failed to set fixed nss param %d: %d\n", + ath10k_warn(ar, "failed to set fixed nss param %d: %d\n", fixed_nss, ret); ret = -EINVAL; goto exit; @@ -4206,7 +4284,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, force_sgi); if (ret) { - ath10k_warn("failed to set sgi param %d: %d\n", + ath10k_warn(ar, "failed to set sgi param %d: %d\n", force_sgi, ret); ret = -EINVAL; goto exit; @@ -4235,14 +4313,14 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, return -EINVAL; if (!ath10k_default_bitrate_mask(ar, band, mask)) { - if (!ath10k_get_fixed_rate_nss(mask, band, + if (!ath10k_get_fixed_rate_nss(ar, mask, band, &fixed_rate, &fixed_nss)) return -EINVAL; } if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) { - ath10k_warn("failed to force SGI usage for default rate settings\n"); + ath10k_warn(ar, "failed to force SGI usage for default rate settings\n"); return -EINVAL; } @@ -4261,7 +4339,7 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); - ath10k_dbg(ATH10K_DBG_MAC, + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", sta->addr, changed, sta->bandwidth, sta->rx_nss, sta->smps_mode); @@ -4280,7 +4358,7 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, bw = WMI_PEER_CHWIDTH_80MHZ; break; case IEEE80211_STA_RX_BW_160: - ath10k_warn("Invalid bandwith %d in rc update for %pM\n", + ath10k_warn(ar, "Invalid bandwith %d in rc update for %pM\n", sta->bandwidth, sta->addr); bw = WMI_PEER_CHWIDTH_20MHZ; break; @@ -4307,7 +4385,7 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, smps = WMI_PEER_SMPS_DYNAMIC; break; case IEEE80211_SMPS_NUM_MODES: - ath10k_warn("Invalid smps %d in sta rc update for %pM\n", + ath10k_warn(ar, "Invalid smps %d in sta rc update for %pM\n", sta->smps_mode, sta->addr); smps = WMI_PEER_SMPS_PS_NONE; break; @@ -4339,9 +4417,10 @@ static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) { + struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - ath10k_dbg(ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n", arvif->vdev_id, sta->addr, tid, action); switch (action) { @@ -4489,12 +4568,12 @@ static struct ieee80211_rate ath10k_rates[] = { #define ath10k_g_rates (ath10k_rates + 0) #define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates)) -struct ath10k *ath10k_mac_create(void) +struct ath10k *ath10k_mac_create(size_t priv_size) { struct ieee80211_hw *hw; struct ath10k *ar; - hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops); + hw = ieee80211_alloc_hw(sizeof(struct ath10k) + priv_size, &ath10k_ops); if (!hw) return NULL; @@ -4669,7 +4748,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id) ath10k_get_arvif_iter, &arvif_iter); if (!arvif_iter.arvif) { - ath10k_warn("No VIF found for vdev %d\n", vdev_id); + ath10k_warn(ar, "No VIF found for vdev %d\n", vdev_id); return NULL; } @@ -4815,19 +4894,19 @@ int ath10k_mac_register(struct ath10k *ar) NL80211_DFS_UNSET); if (!ar->dfs_detector) - ath10k_warn("failed to initialise DFS pattern detector\n"); + ath10k_warn(ar, "failed to initialise DFS pattern detector\n"); } ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, ath10k_reg_notifier); if (ret) { - ath10k_err("failed to initialise regulatory: %i\n", ret); + ath10k_err(ar, "failed to initialise regulatory: %i\n", ret); goto err_free; } ret = ieee80211_register_hw(ar->hw); if (ret) { - ath10k_err("failed to register ieee80211: %d\n", ret); + ath10k_err(ar, "failed to register ieee80211: %d\n", ret); goto err_free; } diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index ef4f84376d7c..6c80eeada3e2 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -26,12 +26,14 @@ struct ath10k_generic_iter { int ret; }; -struct ath10k *ath10k_mac_create(void); +struct ath10k *ath10k_mac_create(size_t priv_size); void ath10k_mac_destroy(struct ath10k *ar); int ath10k_mac_register(struct ath10k *ar); void ath10k_mac_unregister(struct ath10k *ar); struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); -void ath10k_reset_scan(unsigned long ptr); +void __ath10k_scan_finish(struct ath10k *ar); +void ath10k_scan_finish(struct ath10k *ar); +void ath10k_scan_timeout_work(struct work_struct *work); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 3376963a4862..056a35a77133 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -44,13 +44,9 @@ enum ath10k_pci_reset_mode { ATH10K_PCI_RESET_WARM_ONLY = 1, }; -static unsigned int ath10k_pci_target_ps; static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO; static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO; -module_param_named(target_ps, ath10k_pci_target_ps, uint, 0644); -MODULE_PARM_DESC(target_ps, "Enable ath10k Target (SoC) PS option"); - module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644); MODULE_PARM_DESC(irq_mode, "0: auto, 1: legacy, 2: msi (default: 0)"); @@ -71,10 +67,7 @@ static const struct pci_device_id ath10k_pci_id_table[] = { static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, u32 *data); -static int ath10k_pci_post_rx(struct ath10k *ar); -static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, - int num); -static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info); +static void ath10k_pci_buffer_cleanup(struct ath10k *ar); static int ath10k_pci_cold_reset(struct ath10k *ar); static int ath10k_pci_warm_reset(struct ath10k *ar); static int ath10k_pci_wait_for_target_init(struct ath10k *ar); @@ -156,79 +149,175 @@ static const struct ce_attr host_ce_config_wlan[] = { static const struct ce_pipe_config target_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ { - .pipenum = 0, - .pipedir = PIPEDIR_OUT, - .nentries = 32, - .nbytes_max = 256, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* CE1: target->host HTT + HTC control */ { - .pipenum = 1, - .pipedir = PIPEDIR_IN, - .nentries = 32, - .nbytes_max = 512, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(512), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* CE2: target->host WMI */ { - .pipenum = 2, - .pipedir = PIPEDIR_IN, - .nentries = 32, - .nbytes_max = 2048, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* CE3: host->target WMI */ { - .pipenum = 3, - .pipedir = PIPEDIR_OUT, - .nentries = 32, - .nbytes_max = 2048, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* CE4: host->target HTT */ { - .pipenum = 4, - .pipedir = PIPEDIR_OUT, - .nentries = 256, - .nbytes_max = 256, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* NB: 50% of src nentries, since tx has 2 frags */ /* CE5: unused */ { - .pipenum = 5, - .pipedir = PIPEDIR_OUT, - .nentries = 32, - .nbytes_max = 2048, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* CE6: Reserved for target autonomous hif_memcpy */ { - .pipenum = 6, - .pipedir = PIPEDIR_INOUT, - .nentries = 32, - .nbytes_max = 4096, - .flags = CE_ATTR_FLAGS, - .reserved = 0, + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(4096), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), }, /* CE7 used only by Host */ }; +/* + * Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(0), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(1), + }, + { /* not used */ + __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(0), + }, + { /* not used */ + __cpu_to_le32(ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(4), + }, + { + __cpu_to_le32(ATH10K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(1), + }, + + /* (Additions here) */ + + { /* must be last */ + __cpu_to_le32(0), + __cpu_to_le32(0), + __cpu_to_le32(0), + }, +}; + static bool ath10k_pci_irq_pending(struct ath10k *ar) { u32 cause; @@ -270,44 +359,111 @@ static void ath10k_pci_enable_legacy_irq(struct ath10k *ar) PCIE_INTR_ENABLE_ADDRESS); } -static irqreturn_t ath10k_pci_early_irq_handler(int irq, void *arg) +static inline const char *ath10k_pci_get_irq_method(struct ath10k *ar) { - struct ath10k *ar = arg; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - if (ar_pci->num_msi_intrs == 0) { - if (!ath10k_pci_irq_pending(ar)) - return IRQ_NONE; - - ath10k_pci_disable_and_clear_legacy_irq(ar); - } - - tasklet_schedule(&ar_pci->early_irq_tasklet); - - return IRQ_HANDLED; + if (ar_pci->num_msi_intrs > 1) + return "msi-x"; + else if (ar_pci->num_msi_intrs == 1) + return "msi"; + else + return "legacy"; } -static int ath10k_pci_request_early_irq(struct ath10k *ar) +static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) { + struct ath10k *ar = pipe->hif_ce_state; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; + struct sk_buff *skb; + dma_addr_t paddr; int ret; - /* Regardless whether MSI-X/MSI/legacy irqs have been set up the first - * interrupt from irq vector is triggered in all cases for FW - * indication/errors */ - ret = request_irq(ar_pci->pdev->irq, ath10k_pci_early_irq_handler, - IRQF_SHARED, "ath10k_pci (early)", ar); + lockdep_assert_held(&ar_pci->ce_lock); + + skb = dev_alloc_skb(pipe->buf_sz); + if (!skb) + return -ENOMEM; + + WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb"); + + paddr = dma_map_single(ar->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ar->dev, paddr))) { + ath10k_warn(ar, "failed to dma map pci rx buf\n"); + dev_kfree_skb_any(skb); + return -EIO; + } + + ATH10K_SKB_CB(skb)->paddr = paddr; + + ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr); if (ret) { - ath10k_warn("failed to request early irq: %d\n", ret); + ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret); + dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); return ret; } return 0; } -static void ath10k_pci_free_early_irq(struct ath10k *ar) +static void __ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) { - free_irq(ath10k_pci_priv(ar)->pdev->irq, ar); + struct ath10k *ar = pipe->hif_ce_state; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_pipe *ce_pipe = pipe->ce_hdl; + int ret, num; + + lockdep_assert_held(&ar_pci->ce_lock); + + if (pipe->buf_sz == 0) + return; + + if (!ce_pipe->dest_ring) + return; + + num = __ath10k_ce_rx_num_free_bufs(ce_pipe); + while (num--) { + ret = __ath10k_pci_rx_post_buf(pipe); + if (ret) { + ath10k_warn(ar, "failed to post pci rx buf: %d\n", ret); + mod_timer(&ar_pci->rx_post_retry, jiffies + + ATH10K_PCI_RX_POST_RETRY_MS); + break; + } + } +} + +static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) +{ + struct ath10k *ar = pipe->hif_ce_state; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + __ath10k_pci_rx_post_pipe(pipe); + spin_unlock_bh(&ar_pci->ce_lock); +} + +static void ath10k_pci_rx_post(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + spin_lock_bh(&ar_pci->ce_lock); + for (i = 0; i < CE_COUNT; i++) + __ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]); + spin_unlock_bh(&ar_pci->ce_lock); +} + +static void ath10k_pci_rx_replenish_retry(unsigned long ptr) +{ + struct ath10k *ar = (void *)ptr; + + ath10k_pci_rx_post(ar); } /* @@ -376,7 +532,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, nbytes = min_t(unsigned int, remaining_bytes, DIAG_TRANSFER_LIMIT); - ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data); + ret = ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data); if (ret != 0) goto done; @@ -389,10 +545,8 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, * convert it from Target CPU virtual address space * to CE address space */ - ath10k_pci_wake(ar); address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address); - ath10k_pci_sleep(ar); ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0, 0); @@ -448,15 +602,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, } done: - if (ret == 0) { - /* Copy data from allocated DMA buf to caller's buf */ - WARN_ON_ONCE(orig_nbytes & 3); - for (i = 0; i < orig_nbytes / sizeof(__le32); i++) { - ((u32 *)data)[i] = - __le32_to_cpu(((__le32 *)data_buf)[i]); - } - } else - ath10k_warn("failed to read diag value at 0x%x: %d\n", + if (ret == 0) + memcpy(data, data_buf, orig_nbytes); + else + ath10k_warn(ar, "failed to read diag value at 0x%x: %d\n", address, ret); if (data_buf) @@ -466,17 +615,54 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, return ret; } +static int ath10k_pci_diag_read32(struct ath10k *ar, u32 address, u32 *value) +{ + __le32 val = 0; + int ret; + + ret = ath10k_pci_diag_read_mem(ar, address, &val, sizeof(val)); + *value = __le32_to_cpu(val); + + return ret; +} + +static int __ath10k_pci_diag_read_hi(struct ath10k *ar, void *dest, + u32 src, u32 len) +{ + u32 host_addr, addr; + int ret; + + host_addr = host_interest_item_address(src); + + ret = ath10k_pci_diag_read32(ar, host_addr, &addr); + if (ret != 0) { + ath10k_warn(ar, "failed to get memcpy hi address for firmware address %d: %d\n", + src, ret); + return ret; + } + + ret = ath10k_pci_diag_read_mem(ar, addr, dest, len); + if (ret != 0) { + ath10k_warn(ar, "failed to memcpy firmware memory from %d (%d B): %d\n", + addr, len, ret); + return ret; + } + + return 0; +} + +#define ath10k_pci_diag_read_hi(ar, dest, src, len) \ + __ath10k_pci_diag_read_hi(ar, dest, HI_ITEM(src), len); + /* Read 4-byte aligned data from Target memory or register */ static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, u32 *data) { /* Assume range doesn't cross this boundary */ if (address >= DRAM_BASE_ADDRESS) - return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32)); + return ath10k_pci_diag_read32(ar, address, data); - ath10k_pci_wake(ar); *data = ath10k_pci_read32(ar, address); - ath10k_pci_sleep(ar); return 0; } @@ -514,9 +700,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, } /* Copy caller's data to allocated DMA buf */ - WARN_ON_ONCE(orig_nbytes & 3); - for (i = 0; i < orig_nbytes / sizeof(__le32); i++) - ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]); + memcpy(data_buf, data, orig_nbytes); /* * The address supplied by the caller is in the @@ -528,9 +712,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, * to * CE address space */ - ath10k_pci_wake(ar); address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address); - ath10k_pci_sleep(ar); remaining_bytes = orig_nbytes; ce_data = ce_data_base; @@ -539,7 +721,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT); /* Set up to receive directly into Target(!) address */ - ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address); + ret = ath10k_ce_rx_post_buf(ce_diag, NULL, address); if (ret != 0) goto done; @@ -608,66 +790,46 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, } if (ret != 0) - ath10k_warn("failed to write diag value at 0x%x: %d\n", + ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n", address, ret); return ret; } +static int ath10k_pci_diag_write32(struct ath10k *ar, u32 address, u32 value) +{ + __le32 val = __cpu_to_le32(value); + + return ath10k_pci_diag_write_mem(ar, address, &val, sizeof(val)); +} + /* Write 4B data to Target memory or register */ static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address, u32 data) { /* Assume range doesn't cross this boundary */ if (address >= DRAM_BASE_ADDRESS) - return ath10k_pci_diag_write_mem(ar, address, &data, - sizeof(u32)); + return ath10k_pci_diag_write32(ar, address, data); - ath10k_pci_wake(ar); ath10k_pci_write32(ar, address, data); - ath10k_pci_sleep(ar); return 0; } -static bool ath10k_pci_target_is_awake(struct ath10k *ar) +static bool ath10k_pci_is_awake(struct ath10k *ar) { - void __iomem *mem = ath10k_pci_priv(ar)->mem; - u32 val; - val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + - RTC_STATE_ADDRESS); - return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON); + u32 val = ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS); + + return RTC_STATE_V_GET(val) == RTC_STATE_V_ON; } -int ath10k_do_pci_wake(struct ath10k *ar) +static int ath10k_pci_wake_wait(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *pci_addr = ar_pci->mem; int tot_delay = 0; int curr_delay = 5; - if (atomic_read(&ar_pci->keep_awake_count) == 0) { - /* Force AWAKE */ - iowrite32(PCIE_SOC_WAKE_V_MASK, - pci_addr + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); - } - atomic_inc(&ar_pci->keep_awake_count); - - if (ar_pci->verified_awake) - return 0; - - for (;;) { - if (ath10k_pci_target_is_awake(ar)) { - ar_pci->verified_awake = true; + while (tot_delay < PCIE_WAKE_TIMEOUT) { + if (ath10k_pci_is_awake(ar)) return 0; - } - - if (tot_delay > PCIE_WAKE_TIMEOUT) { - ath10k_warn("target took longer %d us to wake up (awake count %d)\n", - PCIE_WAKE_TIMEOUT, - atomic_read(&ar_pci->keep_awake_count)); - return -ETIMEDOUT; - } udelay(curr_delay); tot_delay += curr_delay; @@ -675,20 +837,21 @@ int ath10k_do_pci_wake(struct ath10k *ar) if (curr_delay < 50) curr_delay += 5; } + + return -ETIMEDOUT; } -void ath10k_do_pci_sleep(struct ath10k *ar) +static int ath10k_pci_wake(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *pci_addr = ar_pci->mem; + ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, + PCIE_SOC_WAKE_V_MASK); + return ath10k_pci_wake_wait(ar); +} - if (atomic_dec_and_test(&ar_pci->keep_awake_count)) { - /* Allow sleep */ - ar_pci->verified_awake = false; - iowrite32(PCIE_SOC_WAKE_RESET, - pci_addr + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); - } +static void ath10k_pci_sleep(struct ath10k *ar) +{ + ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, + PCIE_SOC_WAKE_RESET); } /* Called by lower (CE) layer when a send to Target completes. */ @@ -726,19 +889,17 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) unsigned int nbytes, max_nbytes; unsigned int transfer_id; unsigned int flags; - int err, num_replenish = 0; while (ath10k_ce_completed_recv_next(ce_state, &transfer_context, &ce_data, &nbytes, &transfer_id, &flags) == 0) { - num_replenish++; skb = transfer_context; max_nbytes = skb->len + skb_tailroom(skb); dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); if (unlikely(max_nbytes < nbytes)) { - ath10k_warn("rxed more than expected (nbytes %d, max %d)", + ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)", nbytes, max_nbytes); dev_kfree_skb_any(skb); continue; @@ -748,12 +909,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) cb->rx_completion(ar, skb, pipe_info->pipe_num); } - err = ath10k_pci_post_rx_pipe(pipe_info, num_replenish); - if (unlikely(err)) { - /* FIXME: retry */ - ath10k_warn("failed to replenish CE rx ring %d (%d bufs): %d\n", - pipe_info->pipe_num, num_replenish, err); - } + ath10k_pci_rx_post_pipe(pipe_info); } static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, @@ -781,10 +937,10 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, } for (i = 0; i < n_items - 1; i++) { - ath10k_dbg(ATH10K_DBG_PCI, + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci tx item %d paddr 0x%08x len %d n_items %d\n", i, items[i].paddr, items[i].len, n_items); - ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ", + ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci tx data: ", items[i].vaddr, items[i].len); err = ath10k_ce_send_nolock(ce_pipe, @@ -799,10 +955,10 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, /* `i` is equal to `n_items -1` after for() */ - ath10k_dbg(ATH10K_DBG_PCI, + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci tx item %d paddr 0x%08x len %d n_items %d\n", i, items[i].paddr, items[i].len, n_items); - ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, "item data: ", + ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci tx data: ", items[i].vaddr, items[i].len); err = ath10k_ce_send_nolock(ce_pipe, @@ -829,52 +985,64 @@ static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_dbg(ATH10K_DBG_PCI, "pci hif get free queue number\n"); + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get free queue number\n"); return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl); } -static void ath10k_pci_hif_dump_area(struct ath10k *ar) +static void ath10k_pci_dump_registers(struct ath10k *ar, + struct ath10k_fw_crash_data *crash_data) { - u32 reg_dump_area = 0; - u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {}; - u32 host_addr; - int ret; - u32 i; + __le32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {}; + int i, ret; - ath10k_err("firmware crashed!\n"); - ath10k_err("hardware name %s version 0x%x\n", - ar->hw_params.name, ar->target_version); - ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version); + lockdep_assert_held(&ar->data_lock); - host_addr = host_interest_item_address(HI_ITEM(hi_failure_state)); - ret = ath10k_pci_diag_read_mem(ar, host_addr, - ®_dump_area, sizeof(u32)); + ret = ath10k_pci_diag_read_hi(ar, ®_dump_values[0], + hi_failure_state, + REG_DUMP_COUNT_QCA988X * sizeof(__le32)); if (ret) { - ath10k_err("failed to read FW dump area address: %d\n", ret); - return; - } - - ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area); - - ret = ath10k_pci_diag_read_mem(ar, reg_dump_area, - ®_dump_values[0], - REG_DUMP_COUNT_QCA988X * sizeof(u32)); - if (ret != 0) { - ath10k_err("failed to read FW dump area: %d\n", ret); + ath10k_err(ar, "failed to read firmware dump area: %d\n", ret); return; } BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4); - ath10k_err("target Register Dump\n"); + ath10k_err(ar, "firmware register dump:\n"); for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4) - ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n", + ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n", i, - reg_dump_values[i], - reg_dump_values[i + 1], - reg_dump_values[i + 2], - reg_dump_values[i + 3]); + __le32_to_cpu(reg_dump_values[i]), + __le32_to_cpu(reg_dump_values[i + 1]), + __le32_to_cpu(reg_dump_values[i + 2]), + __le32_to_cpu(reg_dump_values[i + 3])); + + if (!crash_data) + return; + + for (i = 0; i < REG_DUMP_COUNT_QCA988X; i++) + crash_data->registers[i] = reg_dump_values[i]; +} + +static void ath10k_pci_fw_crashed_dump(struct ath10k *ar) +{ + struct ath10k_fw_crash_data *crash_data; + char uuid[50]; + + spin_lock_bh(&ar->data_lock); + + crash_data = ath10k_debug_get_new_fw_crash_data(ar); + + if (crash_data) + scnprintf(uuid, sizeof(uuid), "%pUl", &crash_data->uuid); + else + scnprintf(uuid, sizeof(uuid), "n/a"); + + ath10k_err(ar, "firmware crashed! (uuid %s)\n", uuid); + ath10k_print_driver_info(ar); + ath10k_pci_dump_registers(ar, crash_data); + + spin_unlock_bh(&ar->data_lock); queue_work(ar->workqueue, &ar->restart_work); } @@ -882,7 +1050,7 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, int force) { - ath10k_dbg(ATH10K_DBG_PCI, "pci hif send complete check\n"); + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif send complete check\n"); if (!force) { int resources; @@ -910,43 +1078,12 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_dbg(ATH10K_DBG_PCI, "pci hif set callbacks\n"); + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif set callbacks\n"); memcpy(&ar_pci->msg_callbacks_current, callbacks, sizeof(ar_pci->msg_callbacks_current)); } -static int ath10k_pci_setup_ce_irq(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - const struct ce_attr *attr; - struct ath10k_pci_pipe *pipe_info; - int pipe_num, disable_interrupts; - - for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { - pipe_info = &ar_pci->pipe_info[pipe_num]; - - /* Handle Diagnostic CE specially */ - if (pipe_info->ce_hdl == ar_pci->ce_diag) - continue; - - attr = &host_ce_config_wlan[pipe_num]; - - if (attr->src_nentries) { - disable_interrupts = attr->flags & CE_ATTR_DIS_INTR; - ath10k_ce_send_cb_register(pipe_info->ce_hdl, - ath10k_pci_ce_send_done, - disable_interrupts); - } - - if (attr->dest_nentries) - ath10k_ce_recv_cb_register(pipe_info->ce_hdl, - ath10k_pci_ce_recv_data); - } - - return 0; -} - static void ath10k_pci_kill_tasklet(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -954,74 +1091,64 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) tasklet_kill(&ar_pci->intr_tq); tasklet_kill(&ar_pci->msi_fw_err); - tasklet_kill(&ar_pci->early_irq_tasklet); for (i = 0; i < CE_COUNT; i++) tasklet_kill(&ar_pci->pipe_info[i].intr); + + del_timer_sync(&ar_pci->rx_post_retry); } -/* TODO - temporary mapping while we have too few CE's */ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, u8 *ul_pipe, u8 *dl_pipe, int *ul_is_polled, int *dl_is_polled) { - int ret = 0; + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + int i; - ath10k_dbg(ATH10K_DBG_PCI, "pci hif map service\n"); + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif map service\n"); /* polling for received messages not supported */ *dl_is_polled = 0; - switch (service_id) { - case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: - /* - * Host->target HTT gets its own pipe, so it can be polled - * while other pipes are interrupt driven. - */ - *ul_pipe = 4; - /* - * Use the same target->host pipe for HTC ctrl, HTC raw - * streams, and HTT. - */ - *dl_pipe = 1; - break; + for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { + entry = &target_service_to_ce_map_wlan[i]; - case ATH10K_HTC_SVC_ID_RSVD_CTRL: - case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS: - /* - * Note: HTC_RAW_STREAMS_SVC is currently unused, and - * HTC_CTRL_RSVD_SVC could share the same pipe as the - * WMI services. So, if another CE is needed, change - * this to *ul_pipe = 3, which frees up CE 0. - */ - /* *ul_pipe = 3; */ - *ul_pipe = 0; - *dl_pipe = 1; - break; + if (__le32_to_cpu(entry->service_id) != service_id) + continue; - case ATH10K_HTC_SVC_ID_WMI_DATA_BK: - case ATH10K_HTC_SVC_ID_WMI_DATA_BE: - case ATH10K_HTC_SVC_ID_WMI_DATA_VI: - case ATH10K_HTC_SVC_ID_WMI_DATA_VO: - - case ATH10K_HTC_SVC_ID_WMI_CONTROL: - *ul_pipe = 3; - *dl_pipe = 2; - break; - - /* pipe 5 unused */ - /* pipe 6 reserved */ - /* pipe 7 reserved */ - - default: - ret = -1; - break; + switch (__le32_to_cpu(entry->pipedir)) { + case PIPEDIR_NONE: + break; + case PIPEDIR_IN: + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + break; + case PIPEDIR_OUT: + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + break; + case PIPEDIR_INOUT: + WARN_ON(dl_set); + WARN_ON(ul_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + *ul_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + ul_set = true; + break; + } } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + *ul_is_polled = (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0; - return ret; + return 0; } static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, @@ -1029,7 +1156,7 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, { int ul_is_polled, dl_is_polled; - ath10k_dbg(ATH10K_DBG_PCI, "pci hif get default pipe\n"); + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get default pipe\n"); (void)ath10k_pci_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_RSVD_CTRL, @@ -1039,141 +1166,48 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, &dl_is_polled); } -static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, - int num) +static void ath10k_pci_irq_disable(struct ath10k *ar) { - struct ath10k *ar = pipe_info->hif_ce_state; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_ce_pipe *ce_state = pipe_info->ce_hdl; - struct sk_buff *skb; - dma_addr_t ce_data; - int i, ret = 0; + int i; - if (pipe_info->buf_sz == 0) - return 0; + ath10k_ce_disable_interrupts(ar); - for (i = 0; i < num; i++) { - skb = dev_alloc_skb(pipe_info->buf_sz); - if (!skb) { - ath10k_warn("failed to allocate skbuff for pipe %d\n", - num); - ret = -ENOMEM; - goto err; - } + /* Regardless how many interrupts were assigned for MSI the first one + * is always used for firmware indications (crashes). There's no way to + * mask the irq in the device so call disable_irq(). Legacy (shared) + * interrupts can be masked on the device though. + */ + if (ar_pci->num_msi_intrs > 0) + disable_irq(ar_pci->pdev->irq); + else + ath10k_pci_disable_and_clear_legacy_irq(ar); - WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb"); - - ce_data = dma_map_single(ar->dev, skb->data, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - - if (unlikely(dma_mapping_error(ar->dev, ce_data))) { - ath10k_warn("failed to DMA map sk_buff\n"); - dev_kfree_skb_any(skb); - ret = -EIO; - goto err; - } - - ATH10K_SKB_CB(skb)->paddr = ce_data; - - pci_dma_sync_single_for_device(ar_pci->pdev, ce_data, - pipe_info->buf_sz, - PCI_DMA_FROMDEVICE); - - ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb, - ce_data); - if (ret) { - ath10k_warn("failed to enqueue to pipe %d: %d\n", - num, ret); - goto err; - } - } - - return ret; - -err: - ath10k_pci_rx_pipe_cleanup(pipe_info); - return ret; + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + synchronize_irq(ar_pci->pdev->irq + i); } -static int ath10k_pci_post_rx(struct ath10k *ar) +static void ath10k_pci_irq_enable(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_pipe *pipe_info; - const struct ce_attr *attr; - int pipe_num, ret = 0; - for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { - pipe_info = &ar_pci->pipe_info[pipe_num]; - attr = &host_ce_config_wlan[pipe_num]; + ath10k_ce_enable_interrupts(ar); - if (attr->dest_nentries == 0) - continue; - - ret = ath10k_pci_post_rx_pipe(pipe_info, - attr->dest_nentries - 1); - if (ret) { - ath10k_warn("failed to post RX buffer for pipe %d: %d\n", - pipe_num, ret); - - for (; pipe_num >= 0; pipe_num--) { - pipe_info = &ar_pci->pipe_info[pipe_num]; - ath10k_pci_rx_pipe_cleanup(pipe_info); - } - return ret; - } - } - - return 0; + /* See comment in ath10k_pci_irq_disable() */ + if (ar_pci->num_msi_intrs > 0) + enable_irq(ar_pci->pdev->irq); + else + ath10k_pci_enable_legacy_irq(ar); } static int ath10k_pci_hif_start(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret, ret_early; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); - ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n"); + ath10k_pci_irq_enable(ar); + ath10k_pci_rx_post(ar); - ath10k_pci_free_early_irq(ar); - ath10k_pci_kill_tasklet(ar); - - ret = ath10k_pci_request_irq(ar); - if (ret) { - ath10k_warn("failed to post RX buffers for all pipes: %d\n", - ret); - goto err_early_irq; - } - - ret = ath10k_pci_setup_ce_irq(ar); - if (ret) { - ath10k_warn("failed to setup CE interrupts: %d\n", ret); - goto err_stop; - } - - /* Post buffers once to start things off. */ - ret = ath10k_pci_post_rx(ar); - if (ret) { - ath10k_warn("failed to post RX buffers for all pipes: %d\n", - ret); - goto err_stop; - } - - ar_pci->started = 1; return 0; - -err_stop: - ath10k_ce_disable_interrupts(ar); - ath10k_pci_free_irq(ar); - ath10k_pci_kill_tasklet(ar); -err_early_irq: - /* Though there should be no interrupts (device was reset) - * power_down() expects the early IRQ to be installed as per the - * driver lifecycle. */ - ret_early = ath10k_pci_request_early_irq(ar); - if (ret_early) - ath10k_warn("failed to re-enable early irq: %d\n", ret_early); - - return ret; } static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) @@ -1193,10 +1227,6 @@ static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) ar = pipe_info->hif_ce_state; ar_pci = ath10k_pci_priv(ar); - - if (!ar_pci->started) - return; - ce_hdl = pipe_info->ce_hdl; while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf, @@ -1227,10 +1257,6 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) ar = pipe_info->hif_ce_state; ar_pci = ath10k_pci_priv(ar); - - if (!ar_pci->started) - return; - ce_hdl = pipe_info->ce_hdl; while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf, @@ -1275,41 +1301,24 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar) ath10k_ce_deinit_pipe(ar, i); } +static void ath10k_pci_flush(struct ath10k *ar) +{ + ath10k_pci_kill_tasklet(ar); + ath10k_pci_buffer_cleanup(ar); +} + static void ath10k_pci_hif_stop(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); - ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n"); + ath10k_pci_irq_disable(ar); + ath10k_pci_flush(ar); - if (WARN_ON(!ar_pci->started)) - return; - - ret = ath10k_ce_disable_interrupts(ar); - if (ret) - ath10k_warn("failed to disable CE interrupts: %d\n", ret); - - ath10k_pci_free_irq(ar); - ath10k_pci_kill_tasklet(ar); - - ret = ath10k_pci_request_early_irq(ar); - if (ret) - ath10k_warn("failed to re-enable early irq: %d\n", ret); - - /* At this point, asynchronous threads are stopped, the target should - * not DMA nor interrupt. We process the leftovers and then free - * everything else up. */ - - ath10k_pci_buffer_cleanup(ar); - - /* Make the sure the device won't access any structures on the host by - * resetting it. The device was fed with PCI CE ringbuffer - * configuration during init. If ringbuffers are freed and the device - * were to access them this could lead to memory corruption on the - * host. */ + /* Most likely the device has HTT Rx ring configured. The only way to + * prevent the device from accessing (and possible corrupting) host + * memory is to reset the chip now. + */ ath10k_pci_warm_reset(ar); - - ar_pci->started = 0; } static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, @@ -1360,7 +1369,7 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, xfer.wait_for_resp = true; xfer.resp_len = 0; - ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr); + ath10k_ce_rx_post_buf(ce_rx, &xfer, resp_paddr); } ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0); @@ -1418,6 +1427,7 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state) static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) { + struct ath10k *ar = ce_state->ar; struct bmi_xfer *xfer; u32 ce_data; unsigned int nbytes; @@ -1429,7 +1439,7 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) return; if (!xfer->wait_for_resp) { - ath10k_warn("unexpected: BMI data received; ignoring\n"); + ath10k_warn(ar, "unexpected: BMI data received; ignoring\n"); return; } @@ -1456,102 +1466,6 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, return -ETIMEDOUT; } -/* - * Map from service/endpoint to Copy Engine. - * This table is derived from the CE_PCI TABLE, above. - * It is passed to the Target at startup for use by firmware. - */ -static const struct service_to_pipe target_service_to_ce_map_wlan[] = { - { - ATH10K_HTC_SVC_ID_WMI_DATA_VO, - PIPEDIR_OUT, /* out = UL = host -> target */ - 3, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_VO, - PIPEDIR_IN, /* in = DL = target -> host */ - 2, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_BK, - PIPEDIR_OUT, /* out = UL = host -> target */ - 3, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_BK, - PIPEDIR_IN, /* in = DL = target -> host */ - 2, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_BE, - PIPEDIR_OUT, /* out = UL = host -> target */ - 3, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_BE, - PIPEDIR_IN, /* in = DL = target -> host */ - 2, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_VI, - PIPEDIR_OUT, /* out = UL = host -> target */ - 3, - }, - { - ATH10K_HTC_SVC_ID_WMI_DATA_VI, - PIPEDIR_IN, /* in = DL = target -> host */ - 2, - }, - { - ATH10K_HTC_SVC_ID_WMI_CONTROL, - PIPEDIR_OUT, /* out = UL = host -> target */ - 3, - }, - { - ATH10K_HTC_SVC_ID_WMI_CONTROL, - PIPEDIR_IN, /* in = DL = target -> host */ - 2, - }, - { - ATH10K_HTC_SVC_ID_RSVD_CTRL, - PIPEDIR_OUT, /* out = UL = host -> target */ - 0, /* could be moved to 3 (share with WMI) */ - }, - { - ATH10K_HTC_SVC_ID_RSVD_CTRL, - PIPEDIR_IN, /* in = DL = target -> host */ - 1, - }, - { - ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */ - PIPEDIR_OUT, /* out = UL = host -> target */ - 0, - }, - { - ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */ - PIPEDIR_IN, /* in = DL = target -> host */ - 1, - }, - { - ATH10K_HTC_SVC_ID_HTT_DATA_MSG, - PIPEDIR_OUT, /* out = UL = host -> target */ - 4, - }, - { - ATH10K_HTC_SVC_ID_HTT_DATA_MSG, - PIPEDIR_IN, /* in = DL = target -> host */ - 1, - }, - - /* (Additions here) */ - - { /* Must be last */ - 0, - 0, - 0, - }, -}; - /* * Send an interrupt to the device to wake up the Target CPU * so it has an opportunity to notice any changed state. @@ -1565,7 +1479,7 @@ static int ath10k_pci_wake_target_cpu(struct ath10k *ar) CORE_CTRL_ADDRESS, &core_ctrl); if (ret) { - ath10k_warn("failed to read core_ctrl: %d\n", ret); + ath10k_warn(ar, "failed to read core_ctrl: %d\n", ret); return ret; } @@ -1576,7 +1490,7 @@ static int ath10k_pci_wake_target_cpu(struct ath10k *ar) CORE_CTRL_ADDRESS, core_ctrl); if (ret) { - ath10k_warn("failed to set target CPU interrupt mask: %d\n", + ath10k_warn(ar, "failed to set target CPU interrupt mask: %d\n", ret); return ret; } @@ -1605,13 +1519,13 @@ static int ath10k_pci_init_config(struct ath10k *ar) ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr, &pcie_state_targ_addr); if (ret != 0) { - ath10k_err("Failed to get pcie state addr: %d\n", ret); + ath10k_err(ar, "Failed to get pcie state addr: %d\n", ret); return ret; } if (pcie_state_targ_addr == 0) { ret = -EIO; - ath10k_err("Invalid pcie state addr\n"); + ath10k_err(ar, "Invalid pcie state addr\n"); return ret; } @@ -1620,13 +1534,13 @@ static int ath10k_pci_init_config(struct ath10k *ar) pipe_cfg_addr), &pipe_cfg_targ_addr); if (ret != 0) { - ath10k_err("Failed to get pipe cfg addr: %d\n", ret); + ath10k_err(ar, "Failed to get pipe cfg addr: %d\n", ret); return ret; } if (pipe_cfg_targ_addr == 0) { ret = -EIO; - ath10k_err("Invalid pipe cfg addr\n"); + ath10k_err(ar, "Invalid pipe cfg addr\n"); return ret; } @@ -1635,7 +1549,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) sizeof(target_ce_config_wlan)); if (ret != 0) { - ath10k_err("Failed to write pipe cfg: %d\n", ret); + ath10k_err(ar, "Failed to write pipe cfg: %d\n", ret); return ret; } @@ -1644,13 +1558,13 @@ static int ath10k_pci_init_config(struct ath10k *ar) svc_to_pipe_map), &svc_to_pipe_map); if (ret != 0) { - ath10k_err("Failed to get svc/pipe map: %d\n", ret); + ath10k_err(ar, "Failed to get svc/pipe map: %d\n", ret); return ret; } if (svc_to_pipe_map == 0) { ret = -EIO; - ath10k_err("Invalid svc_to_pipe map\n"); + ath10k_err(ar, "Invalid svc_to_pipe map\n"); return ret; } @@ -1658,7 +1572,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) target_service_to_ce_map_wlan, sizeof(target_service_to_ce_map_wlan)); if (ret != 0) { - ath10k_err("Failed to write svc/pipe map: %d\n", ret); + ath10k_err(ar, "Failed to write svc/pipe map: %d\n", ret); return ret; } @@ -1667,18 +1581,17 @@ static int ath10k_pci_init_config(struct ath10k *ar) config_flags), &pcie_config_flags); if (ret != 0) { - ath10k_err("Failed to get pcie config_flags: %d\n", ret); + ath10k_err(ar, "Failed to get pcie config_flags: %d\n", ret); return ret; } pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1; - ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr + + ret = ath10k_pci_diag_write_access(ar, pcie_state_targ_addr + offsetof(struct pcie_state, config_flags), - &pcie_config_flags, - sizeof(pcie_config_flags)); + pcie_config_flags); if (ret != 0) { - ath10k_err("Failed to write pcie config_flags: %d\n", ret); + ath10k_err(ar, "Failed to write pcie config_flags: %d\n", ret); return ret; } @@ -1687,7 +1600,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value); if (ret != 0) { - ath10k_err("Faile to get early alloc val: %d\n", ret); + ath10k_err(ar, "Faile to get early alloc val: %d\n", ret); return ret; } @@ -1699,7 +1612,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value); if (ret != 0) { - ath10k_err("Failed to set early alloc val: %d\n", ret); + ath10k_err(ar, "Failed to set early alloc val: %d\n", ret); return ret; } @@ -1708,7 +1621,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value); if (ret != 0) { - ath10k_err("Failed to get option val: %d\n", ret); + ath10k_err(ar, "Failed to get option val: %d\n", ret); return ret; } @@ -1716,7 +1629,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value); if (ret != 0) { - ath10k_err("Failed to set option val: %d\n", ret); + ath10k_err(ar, "Failed to set option val: %d\n", ret); return ret; } @@ -1730,7 +1643,7 @@ static int ath10k_pci_alloc_ce(struct ath10k *ar) for (i = 0; i < CE_COUNT; i++) { ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]); if (ret) { - ath10k_err("failed to allocate copy engine pipe %d: %d\n", + ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n", i, ret); return ret; } @@ -1761,9 +1674,11 @@ static int ath10k_pci_ce_init(struct ath10k *ar) pipe_info->hif_ce_state = ar; attr = &host_ce_config_wlan[pipe_num]; - ret = ath10k_ce_init_pipe(ar, pipe_num, attr); + ret = ath10k_ce_init_pipe(ar, pipe_num, attr, + ath10k_pci_ce_send_done, + ath10k_pci_ce_recv_data); if (ret) { - ath10k_err("failed to initialize copy engine pipe %d: %d\n", + ath10k_err(ar, "failed to initialize copy engine pipe %d: %d\n", pipe_num, ret); return ret; } @@ -1783,32 +1698,19 @@ static int ath10k_pci_ce_init(struct ath10k *ar) return 0; } -static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) +static bool ath10k_pci_has_fw_crashed(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - u32 fw_indicator; + return ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS) & + FW_IND_EVENT_PENDING; +} - ath10k_pci_wake(ar); +static void ath10k_pci_fw_crashed_clear(struct ath10k *ar) +{ + u32 val; - fw_indicator = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); - - if (fw_indicator & FW_IND_EVENT_PENDING) { - /* ACK: clear Target-side pending event */ - ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, - fw_indicator & ~FW_IND_EVENT_PENDING); - - if (ar_pci->started) { - ath10k_pci_hif_dump_area(ar); - } else { - /* - * Probable Target failure before we're prepared - * to handle it. Generally unexpected. - */ - ath10k_warn("early firmware event indicated\n"); - } - } - - ath10k_pci_sleep(ar); + val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); + val &= ~FW_IND_EVENT_PENDING; + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, val); } /* this function effectively clears target memory controller assert line */ @@ -1833,25 +1735,19 @@ static void ath10k_pci_warm_reset_si0(struct ath10k *ar) static int ath10k_pci_warm_reset(struct ath10k *ar) { - int ret = 0; u32 val; - ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset\n"); - - ret = ath10k_do_pci_wake(ar); - if (ret) { - ath10k_err("failed to wake up target: %d\n", ret); - return ret; - } + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n"); /* debug */ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CAUSE_ADDRESS); - ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", + val); val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CPU_INTR_ADDRESS); - ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", val); /* disable pending irqs */ @@ -1894,11 +1790,12 @@ static int ath10k_pci_warm_reset(struct ath10k *ar) /* debug */ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CAUSE_ADDRESS); - ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", + val); val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CPU_INTR_ADDRESS); - ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", val); /* CPU warm reset */ @@ -1909,20 +1806,18 @@ static int ath10k_pci_warm_reset(struct ath10k *ar) val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS); - ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", + val); msleep(100); - ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset complete\n"); - ath10k_do_pci_sleep(ar); - return ret; + return 0; } static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - const char *irq_mode; int ret; /* @@ -1941,80 +1836,39 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset) ret = ath10k_pci_warm_reset(ar); if (ret) { - ath10k_err("failed to reset target: %d\n", ret); + ath10k_err(ar, "failed to reset target: %d\n", ret); goto err; } - if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - /* Force AWAKE forever */ - ath10k_do_pci_wake(ar); - ret = ath10k_pci_ce_init(ar); if (ret) { - ath10k_err("failed to initialize CE: %d\n", ret); - goto err_ps; - } - - ret = ath10k_ce_disable_interrupts(ar); - if (ret) { - ath10k_err("failed to disable CE interrupts: %d\n", ret); - goto err_ce; - } - - ret = ath10k_pci_init_irq(ar); - if (ret) { - ath10k_err("failed to init irqs: %d\n", ret); - goto err_ce; - } - - ret = ath10k_pci_request_early_irq(ar); - if (ret) { - ath10k_err("failed to request early irq: %d\n", ret); - goto err_deinit_irq; + ath10k_err(ar, "failed to initialize CE: %d\n", ret); + goto err; } ret = ath10k_pci_wait_for_target_init(ar); if (ret) { - ath10k_err("failed to wait for target to init: %d\n", ret); - goto err_free_early_irq; + ath10k_err(ar, "failed to wait for target to init: %d\n", ret); + goto err_ce; } ret = ath10k_pci_init_config(ar); if (ret) { - ath10k_err("failed to setup init config: %d\n", ret); - goto err_free_early_irq; + ath10k_err(ar, "failed to setup init config: %d\n", ret); + goto err_ce; } ret = ath10k_pci_wake_target_cpu(ar); if (ret) { - ath10k_err("could not wake up target CPU: %d\n", ret); - goto err_free_early_irq; + ath10k_err(ar, "could not wake up target CPU: %d\n", ret); + goto err_ce; } - if (ar_pci->num_msi_intrs > 1) - irq_mode = "MSI-X"; - else if (ar_pci->num_msi_intrs == 1) - irq_mode = "MSI"; - else - irq_mode = "legacy"; - - if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) - ath10k_info("pci irq %s irq_mode %d reset_mode %d\n", - irq_mode, ath10k_pci_irq_mode, - ath10k_pci_reset_mode); - return 0; -err_free_early_irq: - ath10k_pci_free_early_irq(ar); -err_deinit_irq: - ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); ath10k_pci_warm_reset(ar); -err_ps: - if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - ath10k_do_pci_sleep(ar); err: return ret; } @@ -2034,7 +1888,7 @@ static int ath10k_pci_hif_power_up_warm(struct ath10k *ar) if (ret == 0) break; - ath10k_warn("failed to warm reset (attempt %d out of %d): %d\n", + ath10k_warn(ar, "failed to warm reset (attempt %d out of %d): %d\n", i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret); } @@ -2045,7 +1899,7 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) { int ret; - ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power up\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n"); /* * Hardware CUS232 version 2 has some issues with cold reset and the @@ -2057,17 +1911,17 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) */ ret = ath10k_pci_hif_power_up_warm(ar); if (ret) { - ath10k_warn("failed to power up target using warm reset: %d\n", + ath10k_warn(ar, "failed to power up target using warm reset: %d\n", ret); if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY) return ret; - ath10k_warn("trying cold reset\n"); + ath10k_warn(ar, "trying cold reset\n"); ret = __ath10k_pci_hif_power_up(ar, true); if (ret) { - ath10k_err("failed to power up target using cold reset too (%d)\n", + ath10k_err(ar, "failed to power up target using cold reset too (%d)\n", ret); return ret; } @@ -2078,18 +1932,9 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) static void ath10k_pci_hif_power_down(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n"); - ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n"); - - ath10k_pci_free_early_irq(ar); - ath10k_pci_kill_tasklet(ar); - ath10k_pci_deinit_irq(ar); - ath10k_pci_ce_deinit(ar); ath10k_pci_warm_reset(ar); - - if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - ath10k_do_pci_sleep(ar); } #ifdef CONFIG_PM @@ -2171,7 +2016,13 @@ static void ath10k_msi_err_tasklet(unsigned long data) { struct ath10k *ar = (struct ath10k *)data; - ath10k_pci_fw_interrupt_handler(ar); + if (!ath10k_pci_has_fw_crashed(ar)) { + ath10k_warn(ar, "received unsolicited fw crash interrupt\n"); + return; + } + + ath10k_pci_fw_crashed_clear(ar); + ath10k_pci_fw_crashed_dump(ar); } /* @@ -2185,7 +2036,8 @@ static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg) int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL; if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_pci->pipe_info)) { - ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id); + ath10k_warn(ar, "unexpected/invalid irq %d ce_id %d\n", irq, + ce_id); return IRQ_HANDLED; } @@ -2232,36 +2084,17 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) return IRQ_HANDLED; } -static void ath10k_pci_early_irq_tasklet(unsigned long data) -{ - struct ath10k *ar = (struct ath10k *)data; - u32 fw_ind; - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_warn("failed to wake target in early irq tasklet: %d\n", - ret); - return; - } - - fw_ind = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); - if (fw_ind & FW_IND_EVENT_PENDING) { - ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, - fw_ind & ~FW_IND_EVENT_PENDING); - ath10k_pci_hif_dump_area(ar); - } - - ath10k_pci_sleep(ar); - ath10k_pci_enable_legacy_irq(ar); -} - static void ath10k_pci_tasklet(unsigned long data) { struct ath10k *ar = (struct ath10k *)data; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */ + if (ath10k_pci_has_fw_crashed(ar)) { + ath10k_pci_fw_crashed_clear(ar); + ath10k_pci_fw_crashed_dump(ar); + return; + } + ath10k_ce_per_engine_service_any(ar); /* Re-enable legacy irq that was disabled in the irq handler */ @@ -2278,7 +2111,7 @@ static int ath10k_pci_request_irq_msix(struct ath10k *ar) ath10k_pci_msi_fw_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("failed to request MSI-X fw irq %d: %d\n", + ath10k_warn(ar, "failed to request MSI-X fw irq %d: %d\n", ar_pci->pdev->irq + MSI_ASSIGN_FW, ret); return ret; } @@ -2288,7 +2121,7 @@ static int ath10k_pci_request_irq_msix(struct ath10k *ar) ath10k_pci_per_engine_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("failed to request MSI-X ce irq %d: %d\n", + ath10k_warn(ar, "failed to request MSI-X ce irq %d: %d\n", ar_pci->pdev->irq + i, ret); for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--) @@ -2311,7 +2144,7 @@ static int ath10k_pci_request_irq_msi(struct ath10k *ar) ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("failed to request MSI irq %d: %d\n", + ath10k_warn(ar, "failed to request MSI irq %d: %d\n", ar_pci->pdev->irq, ret); return ret; } @@ -2328,7 +2161,7 @@ static int ath10k_pci_request_irq_legacy(struct ath10k *ar) ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("failed to request legacy irq %d: %d\n", + ath10k_warn(ar, "failed to request legacy irq %d: %d\n", ar_pci->pdev->irq, ret); return ret; } @@ -2349,7 +2182,7 @@ static int ath10k_pci_request_irq(struct ath10k *ar) return ath10k_pci_request_irq_msix(ar); } - ath10k_warn("unknown irq configuration upon request\n"); + ath10k_warn(ar, "unknown irq configuration upon request\n"); return -EINVAL; } @@ -2372,8 +2205,6 @@ static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar); tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, (unsigned long)ar); - tasklet_init(&ar_pci->early_irq_tasklet, ath10k_pci_early_irq_tasklet, - (unsigned long)ar); for (i = 0; i < CE_COUNT; i++) { ar_pci->pipe_info[i].ar_pci = ar_pci; @@ -2385,18 +2216,16 @@ static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) static int ath10k_pci_init_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - bool msix_supported = test_bit(ATH10K_PCI_FEATURE_MSI_X, - ar_pci->features); int ret; ath10k_pci_init_irq_tasklets(ar); - if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO && - !test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) - ath10k_info("limiting irq mode to: %d\n", ath10k_pci_irq_mode); + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO) + ath10k_info(ar, "limiting irq mode to: %d\n", + ath10k_pci_irq_mode); /* Try MSI-X */ - if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) { + if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO) { ar_pci->num_msi_intrs = MSI_NUM_REQUEST; ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs, ar_pci->num_msi_intrs); @@ -2426,34 +2255,16 @@ static int ath10k_pci_init_irq(struct ath10k *ar) * synchronization checking. */ ar_pci->num_msi_intrs = 0; - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_warn("failed to wake target: %d\n", ret); - return ret; - } - ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); - ath10k_pci_sleep(ar); return 0; } -static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar) +static void ath10k_pci_deinit_irq_legacy(struct ath10k *ar) { - int ret; - - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_warn("failed to wake target: %d\n", ret); - return ret; - } - ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, 0); - ath10k_pci_sleep(ar); - - return 0; } static int ath10k_pci_deinit_irq(struct ath10k *ar) @@ -2462,7 +2273,8 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) switch (ar_pci->num_msi_intrs) { case 0: - return ath10k_pci_deinit_irq_legacy(ar); + ath10k_pci_deinit_irq_legacy(ar); + return 0; case 1: /* fall-through */ case MSI_NUM_REQUEST: @@ -2472,7 +2284,7 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) pci_disable_msi(ar_pci->pdev); } - ath10k_warn("unknown irq configuration upon deinit\n"); + ath10k_warn(ar, "unknown irq configuration upon deinit\n"); return -EINVAL; } @@ -2480,23 +2292,17 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); unsigned long timeout; - int ret; u32 val; - ath10k_dbg(ATH10K_DBG_BOOT, "boot waiting target to initialise\n"); - - ret = ath10k_pci_wake(ar); - if (ret) { - ath10k_err("failed to wake up target for init: %d\n", ret); - return ret; - } + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot waiting target to initialise\n"); timeout = jiffies + msecs_to_jiffies(ATH10K_PCI_TARGET_WAIT); do { val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); - ath10k_dbg(ATH10K_DBG_BOOT, "boot target indicator %x\n", val); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target indicator %x\n", + val); /* target should never return this */ if (val == 0xffffffff) @@ -2511,55 +2317,42 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar) if (ar_pci->num_msi_intrs == 0) /* Fix potential race by repeating CORE_BASE writes */ - ath10k_pci_soc_write32(ar, PCIE_INTR_ENABLE_ADDRESS, - PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL); + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL); mdelay(10); } while (time_before(jiffies, timeout)); if (val == 0xffffffff) { - ath10k_err("failed to read device register, device is gone\n"); - ret = -EIO; - goto out; + ath10k_err(ar, "failed to read device register, device is gone\n"); + return -EIO; } if (val & FW_IND_EVENT_PENDING) { - ath10k_warn("device has crashed during init\n"); - ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, - val & ~FW_IND_EVENT_PENDING); - ath10k_pci_hif_dump_area(ar); - ret = -ECOMM; - goto out; + ath10k_warn(ar, "device has crashed during init\n"); + ath10k_pci_fw_crashed_clear(ar); + ath10k_pci_fw_crashed_dump(ar); + return -ECOMM; } if (!(val & FW_IND_INITIALIZED)) { - ath10k_err("failed to receive initialized event from target: %08x\n", + ath10k_err(ar, "failed to receive initialized event from target: %08x\n", val); - ret = -ETIMEDOUT; - goto out; + return -ETIMEDOUT; } - ath10k_dbg(ATH10K_DBG_BOOT, "boot target initialised\n"); - -out: - ath10k_pci_sleep(ar); - return ret; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target initialised\n"); + return 0; } static int ath10k_pci_cold_reset(struct ath10k *ar) { - int i, ret; + int i; u32 val; - ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset\n"); - - ret = ath10k_do_pci_wake(ar); - if (ret) { - ath10k_err("failed to wake up target: %d\n", - ret); - return ret; - } + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset\n"); /* Put Target, including PCIe, into RESET. */ val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS); @@ -2584,169 +2377,198 @@ static int ath10k_pci_cold_reset(struct ath10k *ar) msleep(1); } - ath10k_do_pci_sleep(ar); - - ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset complete\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset complete\n"); return 0; } -static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) +static int ath10k_pci_claim(struct ath10k *ar) { - int i; - - for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) { - if (!test_bit(i, ar_pci->features)) - continue; - - switch (i) { - case ATH10K_PCI_FEATURE_MSI_X: - ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n"); - break; - case ATH10K_PCI_FEATURE_SOC_POWER_SAVE: - ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n"); - break; - } - } -} - -static int ath10k_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *pci_dev) -{ - void __iomem *mem; - int ret = 0; - struct ath10k *ar; - struct ath10k_pci *ar_pci; - u32 lcr_val, chip_id; - - ath10k_dbg(ATH10K_DBG_PCI, "pci probe\n"); - - ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL); - if (ar_pci == NULL) - return -ENOMEM; - - ar_pci->pdev = pdev; - ar_pci->dev = &pdev->dev; - - switch (pci_dev->device) { - case QCA988X_2_0_DEVICE_ID: - set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); - break; - default: - ret = -ENODEV; - ath10k_err("Unknown device ID: %d\n", pci_dev->device); - goto err_ar_pci; - } - - if (ath10k_pci_target_ps) - set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features); - - ath10k_pci_dump_features(ar_pci); - - ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops); - if (!ar) { - ath10k_err("failed to create driver core\n"); - ret = -EINVAL; - goto err_ar_pci; - } - - ar_pci->ar = ar; - atomic_set(&ar_pci->keep_awake_count, 0); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct pci_dev *pdev = ar_pci->pdev; + u32 lcr_val; + int ret; pci_set_drvdata(pdev, ar); ret = pci_enable_device(pdev); if (ret) { - ath10k_err("failed to enable PCI device: %d\n", ret); - goto err_ar; + ath10k_err(ar, "failed to enable pci device: %d\n", ret); + return ret; } - /* Request MMIO resources */ ret = pci_request_region(pdev, BAR_NUM, "ath"); if (ret) { - ath10k_err("failed to request MMIO region: %d\n", ret); + ath10k_err(ar, "failed to request region BAR%d: %d\n", BAR_NUM, + ret); goto err_device; } - /* - * Target structures have a limit of 32 bit DMA pointers. - * DMA pointers can be wider than 32 bits by default on some systems. - */ + /* Target expects 32 bit DMA. Enforce it. */ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - ath10k_err("failed to set DMA mask to 32-bit: %d\n", ret); + ath10k_err(ar, "failed to set dma mask to 32-bit: %d\n", ret); goto err_region; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - ath10k_err("failed to set consistent DMA mask to 32-bit\n"); + ath10k_err(ar, "failed to set consistent dma mask to 32-bit: %d\n", + ret); goto err_region; } - /* Set bus master bit in PCI_COMMAND to enable DMA */ pci_set_master(pdev); - /* - * Temporary FIX: disable ASPM - * Will be removed after the OTP is programmed - */ + /* Workaround: Disable ASPM */ pci_read_config_dword(pdev, 0x80, &lcr_val); pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00)); /* Arrange for access to Target SoC registers. */ - mem = pci_iomap(pdev, BAR_NUM, 0); - if (!mem) { - ath10k_err("failed to perform IOMAP for BAR%d\n", BAR_NUM); + ar_pci->mem = pci_iomap(pdev, BAR_NUM, 0); + if (!ar_pci->mem) { + ath10k_err(ar, "failed to iomap BAR%d\n", BAR_NUM); ret = -EIO; goto err_master; } - ar_pci->mem = mem; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem); + return 0; + +err_master: + pci_clear_master(pdev); + +err_region: + pci_release_region(pdev, BAR_NUM); + +err_device: + pci_disable_device(pdev); + + return ret; +} + +static void ath10k_pci_release(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct pci_dev *pdev = ar_pci->pdev; + + pci_iounmap(pdev, ar_pci->mem); + pci_release_region(pdev, BAR_NUM); + pci_clear_master(pdev); + pci_disable_device(pdev); +} + +static int ath10k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_dev) +{ + int ret = 0; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + u32 chip_id; + + ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, + &ath10k_pci_hif_ops); + if (!ar) { + dev_err(&pdev->dev, "failed to allocate core\n"); + return -ENOMEM; + } + + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci probe\n"); + + ar_pci = ath10k_pci_priv(ar); + ar_pci->pdev = pdev; + ar_pci->dev = &pdev->dev; + ar_pci->ar = ar; spin_lock_init(&ar_pci->ce_lock); + setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, + (unsigned long)ar); - ret = ath10k_do_pci_wake(ar); + ret = ath10k_pci_claim(ar); if (ret) { - ath10k_err("Failed to get chip id: %d\n", ret); - goto err_iomap; + ath10k_err(ar, "failed to claim device: %d\n", ret); + goto err_core_destroy; + } + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_err(ar, "failed to wake up: %d\n", ret); + goto err_release; } chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); - - ath10k_do_pci_sleep(ar); + if (chip_id == 0xffffffff) { + ath10k_err(ar, "failed to get chip id\n"); + goto err_sleep; + } ret = ath10k_pci_alloc_ce(ar); if (ret) { - ath10k_err("failed to allocate copy engine pipes: %d\n", ret); - goto err_iomap; + ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", + ret); + goto err_sleep; } - ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem); + ath10k_pci_ce_deinit(ar); + + ret = ath10k_ce_disable_interrupts(ar); + if (ret) { + ath10k_err(ar, "failed to disable copy engine interrupts: %d\n", + ret); + goto err_free_ce; + } + + /* Workaround: There's no known way to mask all possible interrupts via + * device CSR. The only way to make sure device doesn't assert + * interrupts is to reset it. Interrupts are then disabled on host + * after handlers are registered. + */ + ath10k_pci_warm_reset(ar); + + ret = ath10k_pci_init_irq(ar); + if (ret) { + ath10k_err(ar, "failed to init irqs: %d\n", ret); + goto err_free_ce; + } + + ath10k_info(ar, "pci irq %s interrupts %d irq_mode %d reset_mode %d\n", + ath10k_pci_get_irq_method(ar), ar_pci->num_msi_intrs, + ath10k_pci_irq_mode, ath10k_pci_reset_mode); + + ret = ath10k_pci_request_irq(ar); + if (ret) { + ath10k_warn(ar, "failed to request irqs: %d\n", ret); + goto err_deinit_irq; + } + + /* This shouldn't race as the device has been reset above. */ + ath10k_pci_irq_disable(ar); ret = ath10k_core_register(ar, chip_id); if (ret) { - ath10k_err("failed to register driver core: %d\n", ret); - goto err_free_ce; + ath10k_err(ar, "failed to register driver core: %d\n", ret); + goto err_free_irq; } return 0; +err_free_irq: + ath10k_pci_free_irq(ar); + +err_deinit_irq: + ath10k_pci_deinit_irq(ar); + err_free_ce: ath10k_pci_free_ce(ar); -err_iomap: - pci_iounmap(pdev, mem); -err_master: - pci_clear_master(pdev); -err_region: - pci_release_region(pdev, BAR_NUM); -err_device: - pci_disable_device(pdev); -err_ar: + +err_sleep: + ath10k_pci_sleep(ar); + +err_release: + ath10k_pci_release(ar); + +err_core_destroy: ath10k_core_destroy(ar); -err_ar_pci: - /* call HIF PCI free here */ - kfree(ar_pci); return ret; } @@ -2756,7 +2578,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev) struct ath10k *ar = pci_get_drvdata(pdev); struct ath10k_pci *ar_pci; - ath10k_dbg(ATH10K_DBG_PCI, "pci remove\n"); + ath10k_dbg(ar, ATH10K_DBG_PCI, "pci remove\n"); if (!ar) return; @@ -2767,15 +2589,13 @@ static void ath10k_pci_remove(struct pci_dev *pdev) return; ath10k_core_unregister(ar); + ath10k_pci_free_irq(ar); + ath10k_pci_deinit_irq(ar); + ath10k_pci_ce_deinit(ar); ath10k_pci_free_ce(ar); - - pci_iounmap(pdev, ar_pci->mem); - pci_release_region(pdev, BAR_NUM); - pci_clear_master(pdev); - pci_disable_device(pdev); - + ath10k_pci_sleep(ar); + ath10k_pci_release(ar); ath10k_core_destroy(ar); - kfree(ar_pci); } MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table); @@ -2793,7 +2613,8 @@ static int __init ath10k_pci_init(void) ret = pci_register_driver(&ath10k_pci_driver); if (ret) - ath10k_err("failed to register PCI driver: %d\n", ret); + printk(KERN_ERR "failed to register ath10k pci driver: %d\n", + ret); return ret; } @@ -2809,5 +2630,5 @@ module_exit(ath10k_pci_exit); MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_2_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_3_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 940129209990..cf36511c7f4d 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -23,9 +23,6 @@ #include "hw.h" #include "ce.h" -/* FW dump area */ -#define REG_DUMP_COUNT_QCA988X 60 - /* * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite */ @@ -103,12 +100,12 @@ struct pcie_state { * NOTE: Structure is shared between Host software and Target firmware! */ struct ce_pipe_config { - u32 pipenum; - u32 pipedir; - u32 nentries; - u32 nbytes_max; - u32 flags; - u32 reserved; + __le32 pipenum; + __le32 pipedir; + __le32 nentries; + __le32 nbytes_max; + __le32 flags; + __le32 reserved; }; /* @@ -130,17 +127,9 @@ struct ce_pipe_config { /* Establish a mapping between a service/direction and a pipe. */ struct service_to_pipe { - u32 service_id; - u32 pipedir; - u32 pipenum; -}; - -enum ath10k_pci_features { - ATH10K_PCI_FEATURE_MSI_X = 0, - ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 1, - - /* keep last */ - ATH10K_PCI_FEATURE_COUNT + __le32 service_id; + __le32 pipedir; + __le32 pipenum; }; /* Per-pipe state. */ @@ -169,8 +158,6 @@ struct ath10k_pci { struct ath10k *ar; void __iomem *mem; - DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT); - /* * Number of MSI interrupts granted, 0 --> using legacy PCI line * interrupts. @@ -179,12 +166,6 @@ struct ath10k_pci { struct tasklet_struct intr_tq; struct tasklet_struct msi_fw_err; - struct tasklet_struct early_irq_tasklet; - - int started; - - atomic_t keep_awake_count; - bool verified_awake; struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX]; @@ -198,27 +179,15 @@ struct ath10k_pci { /* Map CE id to ce_state */ struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; + struct timer_list rx_post_retry; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) { - return ar->hif.priv; -} - -static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); -} - -static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); + return (struct ath10k_pci *)ar->drv_priv; } +#define ATH10K_PCI_RX_POST_RETRY_MS 50 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ #define PCIE_WAKE_TIMEOUT 5000 /* 5ms */ @@ -242,35 +211,17 @@ static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) /* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ #define DIAG_ACCESS_CE_TIMEOUT_MS 10 -/* - * This API allows the Host to access Target registers directly - * and relatively efficiently over PCIe. - * This allows the Host to avoid extra overhead associated with - * sending a message to firmware and waiting for a response message - * from firmware, as is done on other interconnects. +/* Target exposes its registers for direct access. However before host can + * access them it needs to make sure the target is awake (ath10k_pci_wake, + * ath10k_pci_wake_wait, ath10k_pci_is_awake). Once target is awake it won't go + * to sleep unless host tells it to (ath10k_pci_sleep). * - * Yet there is some complexity with direct accesses because the - * Target's power state is not known a priori. The Host must issue - * special PCIe reads/writes in order to explicitly wake the Target - * and to verify that it is awake and will remain awake. + * If host tries to access target registers without waking it up it can + * scribble over host memory. * - * Usage: - * - * Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space. - * These calls must be bracketed by ath10k_pci_wake and - * ath10k_pci_sleep. A single BEGIN/END pair is adequate for - * multiple READ/WRITE operations. - * - * Use ath10k_pci_wake to put the Target in a state in - * which it is legal for the Host to directly access it. This - * may involve waking the Target from a low power state, which - * may take up to 2Ms! - * - * Use ath10k_pci_sleep to tell the Target that as far as - * this code path is concerned, it no longer needs to remain - * directly accessible. BEGIN/END is under a reference counter; - * multiple code paths may issue BEGIN/END on a single targid. + * If target is asleep waking it up may take up to even 2ms. */ + static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) { @@ -296,25 +247,18 @@ static inline void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val) ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + addr, val); } -int ath10k_do_pci_wake(struct ath10k *ar); -void ath10k_do_pci_sleep(struct ath10k *ar); - -static inline int ath10k_pci_wake(struct ath10k *ar) +static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - return ath10k_do_pci_wake(ar); - - return 0; + return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); } -static inline void ath10k_pci_sleep(struct ath10k *ar) +static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - ath10k_do_pci_sleep(ar); + iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); } #endif /* _PCI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c new file mode 100644 index 000000000000..3e1454b74e00 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "core.h" +#include "debug.h" + +static void send_fft_sample(struct ath10k *ar, + const struct fft_sample_tlv *fft_sample_tlv) +{ + int length; + + if (!ar->spectral.rfs_chan_spec_scan) + return; + + length = __be16_to_cpu(fft_sample_tlv->length) + + sizeof(*fft_sample_tlv); + relay_write(ar->spectral.rfs_chan_spec_scan, fft_sample_tlv, length); +} + +static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len, + u8 *data) +{ + int dc_pos; + u8 max_exp; + + dc_pos = bin_len / 2; + + /* peak index outside of bins */ + if (dc_pos < max_index || -dc_pos >= max_index) + return 0; + + for (max_exp = 0; max_exp < 8; max_exp++) { + if (data[dc_pos + max_index] == (max_magnitude >> max_exp)) + break; + } + + /* max_exp not found */ + if (data[dc_pos + max_index] != (max_magnitude >> max_exp)) + return 0; + + return max_exp; +} + +int ath10k_spectral_process_fft(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + struct phyerr_fft_report *fftr, + size_t bin_len, u64 tsf) +{ + struct fft_sample_ath10k *fft_sample; + u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS]; + u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag; + u32 reg0, reg1, nf_list1, nf_list2; + u8 chain_idx, *bins; + int dc_pos; + + fft_sample = (struct fft_sample_ath10k *)&buf; + + if (bin_len < 64 || bin_len > SPECTRAL_ATH10K_MAX_NUM_BINS) + return -EINVAL; + + reg0 = __le32_to_cpu(fftr->reg0); + reg1 = __le32_to_cpu(fftr->reg1); + + length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + bin_len; + fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH10K; + fft_sample->tlv.length = __cpu_to_be16(length); + + /* TODO: there might be a reason why the hardware reports 20/40/80 MHz, + * but the results/plots suggest that its actually 22/44/88 MHz. + */ + switch (event->hdr.chan_width_mhz) { + case 20: + fft_sample->chan_width_mhz = 22; + break; + case 40: + fft_sample->chan_width_mhz = 44; + break; + case 80: + /* TODO: As experiments with an analogue sender and various + * configuaritions (fft-sizes of 64/128/256 and 20/40/80 Mhz) + * show, the particular configuration of 80 MHz/64 bins does + * not match with the other smaples at all. Until the reason + * for that is found, don't report these samples. + */ + if (bin_len == 64) + return -EINVAL; + fft_sample->chan_width_mhz = 88; + break; + default: + fft_sample->chan_width_mhz = event->hdr.chan_width_mhz; + } + + fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB); + fft_sample->avgpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB); + + peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG); + fft_sample->max_magnitude = __cpu_to_be16(peak_mag); + fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX); + fft_sample->rssi = event->hdr.rssi_combined; + + total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB); + base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB); + fft_sample->total_gain_db = __cpu_to_be16(total_gain_db); + fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db); + + freq1 = __le16_to_cpu(event->hdr.freq1); + freq2 = __le16_to_cpu(event->hdr.freq2); + fft_sample->freq1 = __cpu_to_be16(freq1); + fft_sample->freq2 = __cpu_to_be16(freq2); + + nf_list1 = __le32_to_cpu(event->hdr.nf_list_1); + nf_list2 = __le32_to_cpu(event->hdr.nf_list_2); + chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX); + + switch (chain_idx) { + case 0: + fft_sample->noise = __cpu_to_be16(nf_list1 & 0xffffu); + break; + case 1: + fft_sample->noise = __cpu_to_be16((nf_list1 >> 16) & 0xffffu); + break; + case 2: + fft_sample->noise = __cpu_to_be16(nf_list2 & 0xffffu); + break; + case 3: + fft_sample->noise = __cpu_to_be16((nf_list2 >> 16) & 0xffffu); + break; + } + + bins = (u8 *)fftr; + bins += sizeof(*fftr); + + fft_sample->tsf = __cpu_to_be64(tsf); + + /* max_exp has been directly reported by previous hardware (ath9k), + * maybe its possible to get it by other means? + */ + fft_sample->max_exp = get_max_exp(fft_sample->max_index, peak_mag, + bin_len, bins); + + memcpy(fft_sample->data, bins, bin_len); + + /* DC value (value in the middle) is the blind spot of the spectral + * sample and invalid, interpolate it. + */ + dc_pos = bin_len / 2; + fft_sample->data[dc_pos] = (fft_sample->data[dc_pos + 1] + + fft_sample->data[dc_pos - 1]) / 2; + + send_fft_sample(ar, &fft_sample->tlv); + + return 0; +} + +static struct ath10k_vif *ath10k_get_spectral_vdev(struct ath10k *ar) +{ + struct ath10k_vif *arvif; + + lockdep_assert_held(&ar->conf_mutex); + + if (list_empty(&ar->arvifs)) + return NULL; + + /* if there already is a vif doing spectral, return that. */ + list_for_each_entry(arvif, &ar->arvifs, list) + if (arvif->spectral_enabled) + return arvif; + + /* otherwise, return the first vif. */ + return list_first_entry(&ar->arvifs, typeof(*arvif), list); +} + +static int ath10k_spectral_scan_trigger(struct ath10k *ar) +{ + struct ath10k_vif *arvif; + int res; + int vdev_id; + + lockdep_assert_held(&ar->conf_mutex); + + arvif = ath10k_get_spectral_vdev(ar); + if (!arvif) + return -ENODEV; + vdev_id = arvif->vdev_id; + + if (ar->spectral.mode == SPECTRAL_DISABLED) + return 0; + + res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id, + WMI_SPECTRAL_TRIGGER_CMD_CLEAR, + WMI_SPECTRAL_ENABLE_CMD_ENABLE); + if (res < 0) + return res; + + res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id, + WMI_SPECTRAL_TRIGGER_CMD_TRIGGER, + WMI_SPECTRAL_ENABLE_CMD_ENABLE); + if (res < 0) + return res; + + return 0; +} + +static int ath10k_spectral_scan_config(struct ath10k *ar, + enum ath10k_spectral_mode mode) +{ + struct wmi_vdev_spectral_conf_arg arg; + struct ath10k_vif *arvif; + int vdev_id, count, res = 0; + + lockdep_assert_held(&ar->conf_mutex); + + arvif = ath10k_get_spectral_vdev(ar); + if (!arvif) + return -ENODEV; + + vdev_id = arvif->vdev_id; + + arvif->spectral_enabled = (mode != SPECTRAL_DISABLED); + ar->spectral.mode = mode; + + res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id, + WMI_SPECTRAL_TRIGGER_CMD_CLEAR, + WMI_SPECTRAL_ENABLE_CMD_DISABLE); + if (res < 0) { + ath10k_warn(ar, "failed to enable spectral scan: %d\n", res); + return res; + } + + if (mode == SPECTRAL_DISABLED) + return 0; + + if (mode == SPECTRAL_BACKGROUND) + count = WMI_SPECTRAL_COUNT_DEFAULT; + else + count = max_t(u8, 1, ar->spectral.config.count); + + arg.vdev_id = vdev_id; + arg.scan_count = count; + arg.scan_period = WMI_SPECTRAL_PERIOD_DEFAULT; + arg.scan_priority = WMI_SPECTRAL_PRIORITY_DEFAULT; + arg.scan_fft_size = ar->spectral.config.fft_size; + arg.scan_gc_ena = WMI_SPECTRAL_GC_ENA_DEFAULT; + arg.scan_restart_ena = WMI_SPECTRAL_RESTART_ENA_DEFAULT; + arg.scan_noise_floor_ref = WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT; + arg.scan_init_delay = WMI_SPECTRAL_INIT_DELAY_DEFAULT; + arg.scan_nb_tone_thr = WMI_SPECTRAL_NB_TONE_THR_DEFAULT; + arg.scan_str_bin_thr = WMI_SPECTRAL_STR_BIN_THR_DEFAULT; + arg.scan_wb_rpt_mode = WMI_SPECTRAL_WB_RPT_MODE_DEFAULT; + arg.scan_rssi_rpt_mode = WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT; + arg.scan_rssi_thr = WMI_SPECTRAL_RSSI_THR_DEFAULT; + arg.scan_pwr_format = WMI_SPECTRAL_PWR_FORMAT_DEFAULT; + arg.scan_rpt_mode = WMI_SPECTRAL_RPT_MODE_DEFAULT; + arg.scan_bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT; + arg.scan_dbm_adj = WMI_SPECTRAL_DBM_ADJ_DEFAULT; + arg.scan_chn_mask = WMI_SPECTRAL_CHN_MASK_DEFAULT; + + res = ath10k_wmi_vdev_spectral_conf(ar, &arg); + if (res < 0) { + ath10k_warn(ar, "failed to configure spectral scan: %d\n", res); + return res; + } + + return 0; +} + +static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char *mode = ""; + unsigned int len; + enum ath10k_spectral_mode spectral_mode; + + mutex_lock(&ar->conf_mutex); + spectral_mode = ar->spectral.mode; + mutex_unlock(&ar->conf_mutex); + + switch (spectral_mode) { + case SPECTRAL_DISABLED: + mode = "disable"; + break; + case SPECTRAL_BACKGROUND: + mode = "background"; + break; + case SPECTRAL_MANUAL: + mode = "manual"; + break; + } + + len = strlen(mode); + return simple_read_from_buffer(user_buf, count, ppos, mode, len); +} + +static ssize_t write_file_spec_scan_ctl(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + ssize_t len; + int res; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + mutex_lock(&ar->conf_mutex); + + if (strncmp("trigger", buf, 7) == 0) { + if (ar->spectral.mode == SPECTRAL_MANUAL || + ar->spectral.mode == SPECTRAL_BACKGROUND) { + /* reset the configuration to adopt possibly changed + * debugfs parameters + */ + res = ath10k_spectral_scan_config(ar, + ar->spectral.mode); + if (res < 0) { + ath10k_warn(ar, "failed to reconfigure spectral scan: %d\n", + res); + } + res = ath10k_spectral_scan_trigger(ar); + if (res < 0) { + ath10k_warn(ar, "failed to trigger spectral scan: %d\n", + res); + } + } else { + res = -EINVAL; + } + } else if (strncmp("background", buf, 9) == 0) { + res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND); + } else if (strncmp("manual", buf, 6) == 0) { + res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL); + } else if (strncmp("disable", buf, 7) == 0) { + res = ath10k_spectral_scan_config(ar, SPECTRAL_DISABLED); + } else { + res = -EINVAL; + } + + mutex_unlock(&ar->conf_mutex); + + if (res < 0) + return res; + + return count; +} + +static const struct file_operations fops_spec_scan_ctl = { + .read = read_file_spec_scan_ctl, + .write = write_file_spec_scan_ctl, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_spectral_count(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + unsigned int len; + u8 spectral_count; + + mutex_lock(&ar->conf_mutex); + spectral_count = ar->spectral.config.count; + mutex_unlock(&ar->conf_mutex); + + len = sprintf(buf, "%d\n", spectral_count); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_count(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + ar->spectral.config.count = val; + mutex_unlock(&ar->conf_mutex); + + return count; +} + +static const struct file_operations fops_spectral_count = { + .read = read_file_spectral_count, + .write = write_file_spectral_count, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_spectral_bins(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + unsigned int len, bins, fft_size, bin_scale; + + mutex_lock(&ar->conf_mutex); + + fft_size = ar->spectral.config.fft_size; + bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT; + bins = 1 << (fft_size - bin_scale); + + mutex_unlock(&ar->conf_mutex); + + len = sprintf(buf, "%d\n", bins); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_bins(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 64 || val > SPECTRAL_ATH10K_MAX_NUM_BINS) + return -EINVAL; + + if (!is_power_of_2(val)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + ar->spectral.config.fft_size = ilog2(val); + ar->spectral.config.fft_size += WMI_SPECTRAL_BIN_SCALE_DEFAULT; + mutex_unlock(&ar->conf_mutex); + + return count; +} + +static const struct file_operations fops_spectral_bins = { + .read = read_file_spectral_bins, + .write = write_file_spectral_bins, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *buf_file; + + buf_file = debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); + *is_global = 1; + return buf_file; +} + +static int remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + + return 0; +} + +static struct rchan_callbacks rfs_spec_scan_cb = { + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + +int ath10k_spectral_start(struct ath10k *ar) +{ + struct ath10k_vif *arvif; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) + arvif->spectral_enabled = 0; + + ar->spectral.mode = SPECTRAL_DISABLED; + ar->spectral.config.count = WMI_SPECTRAL_COUNT_DEFAULT; + ar->spectral.config.fft_size = WMI_SPECTRAL_FFT_SIZE_DEFAULT; + + return 0; +} + +int ath10k_spectral_vif_stop(struct ath10k_vif *arvif) +{ + if (!arvif->spectral_enabled) + return 0; + + return ath10k_spectral_scan_config(arvif->ar, SPECTRAL_DISABLED); +} + +int ath10k_spectral_create(struct ath10k *ar) +{ + ar->spectral.rfs_chan_spec_scan = relay_open("spectral_scan", + ar->debug.debugfs_phy, + 1024, 256, + &rfs_spec_scan_cb, NULL); + debugfs_create_file("spectral_scan_ctl", + S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, + &fops_spec_scan_ctl); + debugfs_create_file("spectral_count", + S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, + &fops_spectral_count); + debugfs_create_file("spectral_bins", + S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, + &fops_spectral_bins); + + return 0; +} + +void ath10k_spectral_destroy(struct ath10k *ar) +{ + if (ar->spectral.rfs_chan_spec_scan) { + relay_close(ar->spectral.rfs_chan_spec_scan); + ar->spectral.rfs_chan_spec_scan = NULL; + } +} diff --git a/drivers/net/wireless/ath/ath10k/spectral.h b/drivers/net/wireless/ath/ath10k/spectral.h new file mode 100644 index 000000000000..ddc57c557272 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/spectral.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SPECTRAL_H +#define SPECTRAL_H + +#include "../spectral_common.h" + +/** + * struct ath10k_spec_scan - parameters for Atheros spectral scan + * + * @count: number of scan results requested for manual mode + * @fft_size: number of bins to be requested = 2^(fft_size - bin_scale) + */ +struct ath10k_spec_scan { + u8 count; + u8 fft_size; +}; + +/* enum ath10k_spectral_mode: + * + * @SPECTRAL_DISABLED: spectral mode is disabled + * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with + * something else. + * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples + * is performed manually. + */ +enum ath10k_spectral_mode { + SPECTRAL_DISABLED = 0, + SPECTRAL_BACKGROUND, + SPECTRAL_MANUAL, +}; + +#ifdef CONFIG_ATH10K_DEBUGFS + +int ath10k_spectral_process_fft(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + struct phyerr_fft_report *fftr, + size_t bin_len, u64 tsf); +int ath10k_spectral_start(struct ath10k *ar); +int ath10k_spectral_vif_stop(struct ath10k_vif *arvif); +int ath10k_spectral_create(struct ath10k *ar); +void ath10k_spectral_destroy(struct ath10k *ar); + +#else + +static inline int +ath10k_spectral_process_fft(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + struct phyerr_fft_report *fftr, + size_t bin_len, u64 tsf) +{ + return 0; +} + +static inline int ath10k_spectral_start(struct ath10k *ar) +{ + return 0; +} + +static inline int ath10k_spectral_vif_stop(struct ath10k_vif *arvif) +{ + return 0; +} + +static inline int ath10k_spectral_create(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_spectral_destroy(struct ath10k *ar) +{ +} + +#endif /* CONFIG_ATH10K_DEBUGFS */ + +#endif /* SPECTRAL_H */ diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index f4fa22d1d591..2eeec8a63d5c 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -32,14 +32,14 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) * offchan_tx_skb. */ spin_lock_bh(&ar->data_lock); if (ar->offchan_tx_skb != skb) { - ath10k_warn("completed old offchannel frame\n"); + ath10k_warn(ar, "completed old offchannel frame\n"); goto out; } complete(&ar->offchan_tx_completed); ar->offchan_tx_skb = NULL; /* just for sanity */ - ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb); + ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb); out: spin_unlock_bh(&ar->data_lock); } @@ -47,18 +47,19 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) void ath10k_txrx_tx_unref(struct ath10k_htt *htt, const struct htt_tx_done *tx_done) { - struct device *dev = htt->ar->dev; + struct ath10k *ar = htt->ar; + struct device *dev = ar->dev; struct ieee80211_tx_info *info; struct ath10k_skb_cb *skb_cb; struct sk_buff *msdu; lockdep_assert_held(&htt->tx_lock); - ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n", tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack); if (tx_done->msdu_id >= htt->max_num_pending_tx) { - ath10k_warn("warning: msdu_id %d too big, ignoring\n", + ath10k_warn(ar, "warning: msdu_id %d too big, ignoring\n", tx_done->msdu_id); return; } @@ -182,7 +183,7 @@ void ath10k_peer_map_event(struct ath10k_htt *htt, wake_up(&ar->peer_mapping_wq); } - ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n", ev->vdev_id, ev->addr, ev->peer_id); set_bit(ev->peer_id, peer->peer_ids); @@ -199,12 +200,12 @@ void ath10k_peer_unmap_event(struct ath10k_htt *htt, spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find_by_id(ar, ev->peer_id); if (!peer) { - ath10k_warn("peer-unmap-event: unknown peer id %d\n", + ath10k_warn(ar, "peer-unmap-event: unknown peer id %d\n", ev->peer_id); goto exit; } - ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n", + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n", peer->vdev_id, peer->addr, ev->peer_id); clear_bit(ev->peer_id, peer->peer_ids); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index c2c87c916b5a..e500a3cc905e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -487,6 +487,127 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, }; +/* firmware 10.2 specific mappings */ +static struct wmi_cmd_map wmi_10_2_cmd_map = { + .init_cmdid = WMI_10_2_INIT_CMDID, + .start_scan_cmdid = WMI_10_2_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, + .peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = + WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED, + .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED, + .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED, + .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED, + .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED, + .echo_cmdid = WMI_10_2_ECHO_CMDID, + .pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, + .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, +}; + int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { int ret; @@ -503,18 +624,18 @@ int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar) return ret; } -static struct sk_buff *ath10k_wmi_alloc_skb(u32 len) +static struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len) { struct sk_buff *skb; u32 round_len = roundup(len, 4); - skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len); + skb = ath10k_htc_alloc_skb(ar, WMI_SKB_HEADROOM + round_len); if (!skb) return NULL; skb_reserve(skb, WMI_SKB_HEADROOM); if (!IS_ALIGNED((unsigned long)skb->data, 4)) - ath10k_warn("Unaligned WMI skb\n"); + ath10k_warn(ar, "Unaligned WMI skb\n"); skb_put(skb, round_len); memset(skb->data, 0, round_len); @@ -612,7 +733,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, might_sleep(); if (cmd_id == WMI_CMD_UNSUPPORTED) { - ath10k_warn("wmi command %d is not supported by firmware\n", + ath10k_warn(ar, "wmi command %d is not supported by firmware\n", cmd_id); return ret; } @@ -660,7 +781,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) len = round_up(len, 4); - wmi_skb = ath10k_wmi_alloc_skb(len); + wmi_skb = ath10k_wmi_alloc_skb(ar, len); if (!wmi_skb) return -ENOMEM; @@ -674,7 +795,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN); memcpy(cmd->buf, skb->data, skb->len); - ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); @@ -690,6 +811,130 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) return ret; } +static void ath10k_wmi_event_scan_started(struct ath10k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + ath10k_warn(ar, "received scan started event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_STARTING: + ar->scan.state = ATH10K_SCAN_RUNNING; + + if (ar->scan.is_roc) + ieee80211_ready_on_channel(ar->hw); + + complete(&ar->scan.started); + break; + } +} + +static void ath10k_wmi_event_scan_completed(struct ath10k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + /* One suspected reason scan can be completed while starting is + * if firmware fails to deliver all scan events to the host, + * e.g. when transport pipe is full. This has been observed + * with spectral scan phyerr events starving wmi transport + * pipe. In such case the "scan completed" event should be (and + * is) ignored by the host as it may be just firmware's scan + * state machine recovering. + */ + ath10k_warn(ar, "received scan completed event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + __ath10k_scan_finish(ar); + break; + } +} + +static void ath10k_wmi_event_scan_bss_chan(struct ath10k *ar) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + ath10k_warn(ar, "received scan bss chan event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + ar->scan_channel = NULL; + break; + } +} + +static void ath10k_wmi_event_scan_foreign_chan(struct ath10k *ar, u32 freq) +{ + lockdep_assert_held(&ar->data_lock); + + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + ath10k_warn(ar, "received scan foreign chan event in an invalid scan state: %s (%d)\n", + ath10k_scan_state_str(ar->scan.state), + ar->scan.state); + break; + case ATH10K_SCAN_RUNNING: + case ATH10K_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; + } +} + +static const char * +ath10k_wmi_event_scan_type_str(enum wmi_scan_event_type type, + enum wmi_scan_completion_reason reason) +{ + switch (type) { + case WMI_SCAN_EVENT_STARTED: + return "started"; + case WMI_SCAN_EVENT_COMPLETED: + switch (reason) { + case WMI_SCAN_REASON_COMPLETED: + return "completed"; + case WMI_SCAN_REASON_CANCELLED: + return "completed [cancelled]"; + case WMI_SCAN_REASON_PREEMPTED: + return "completed [preempted]"; + case WMI_SCAN_REASON_TIMEDOUT: + return "completed [timedout]"; + case WMI_SCAN_REASON_MAX: + break; + } + return "completed [unknown]"; + case WMI_SCAN_EVENT_BSS_CHANNEL: + return "bss channel"; + case WMI_SCAN_EVENT_FOREIGN_CHANNEL: + return "foreign channel"; + case WMI_SCAN_EVENT_DEQUEUED: + return "dequeued"; + case WMI_SCAN_EVENT_PREEMPTED: + return "preempted"; + case WMI_SCAN_EVENT_START_FAILED: + return "start failed"; + default: + return "unknown"; + } +} + static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) { struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; @@ -707,81 +952,32 @@ static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) scan_id = __le32_to_cpu(event->scan_id); vdev_id = __le32_to_cpu(event->vdev_id); - ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n"); - ath10k_dbg(ATH10K_DBG_WMI, - "scan event type %d reason %d freq %d req_id %d " - "scan_id %d vdev_id %d\n", - event_type, reason, freq, req_id, scan_id, vdev_id); - spin_lock_bh(&ar->data_lock); + ath10k_dbg(ar, ATH10K_DBG_WMI, + "scan event %s type %d reason %d freq %d req_id %d scan_id %d vdev_id %d state %s (%d)\n", + ath10k_wmi_event_scan_type_str(event_type, reason), + event_type, reason, freq, req_id, scan_id, vdev_id, + ath10k_scan_state_str(ar->scan.state), ar->scan.state); + switch (event_type) { case WMI_SCAN_EVENT_STARTED: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n"); - if (ar->scan.in_progress && ar->scan.is_roc) - ieee80211_ready_on_channel(ar->hw); - - complete(&ar->scan.started); + ath10k_wmi_event_scan_started(ar); break; case WMI_SCAN_EVENT_COMPLETED: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n"); - switch (reason) { - case WMI_SCAN_REASON_COMPLETED: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n"); - break; - case WMI_SCAN_REASON_CANCELLED: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n"); - break; - case WMI_SCAN_REASON_PREEMPTED: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n"); - break; - case WMI_SCAN_REASON_TIMEDOUT: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n"); - break; - default: - break; - } - - ar->scan_channel = NULL; - if (!ar->scan.in_progress) { - ath10k_warn("no scan requested, ignoring\n"); - break; - } - - if (ar->scan.is_roc) { - ath10k_offchan_tx_purge(ar); - - if (!ar->scan.aborting) - ieee80211_remain_on_channel_expired(ar->hw); - } else { - ieee80211_scan_completed(ar->hw, ar->scan.aborting); - } - - del_timer(&ar->scan.timeout); - complete_all(&ar->scan.completed); - ar->scan.in_progress = false; + ath10k_wmi_event_scan_completed(ar); break; case WMI_SCAN_EVENT_BSS_CHANNEL: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n"); - ar->scan_channel = NULL; + ath10k_wmi_event_scan_bss_chan(ar); break; case WMI_SCAN_EVENT_FOREIGN_CHANNEL: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n"); - ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); - if (ar->scan.in_progress && ar->scan.is_roc && - ar->scan.roc_freq == freq) { - complete(&ar->scan.on_channel); - } - break; - case WMI_SCAN_EVENT_DEQUEUED: - ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n"); - break; - case WMI_SCAN_EVENT_PREEMPTED: - ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n"); + ath10k_wmi_event_scan_foreign_chan(ar, freq); break; case WMI_SCAN_EVENT_START_FAILED: - ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n"); + ath10k_warn(ar, "received scan start failure event\n"); break; + case WMI_SCAN_EVENT_DEQUEUED: + case WMI_SCAN_EVENT_PREEMPTED: default: break; } @@ -911,7 +1107,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) memset(status, 0, sizeof(*status)); - ath10k_dbg(ATH10K_DBG_MGMT, + ath10k_dbg(ar, ATH10K_DBG_MGMT, "event mgmt rx status %08x\n", rx_status); if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) { @@ -947,9 +1143,9 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) if (phy_mode == MODE_11B && status->band == IEEE80211_BAND_5GHZ) - ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n"); + ath10k_dbg(ar, ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n"); } else { - ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n"); + ath10k_warn(ar, "using (unreliable) phy_mode to extract band for mgmt rx\n"); status->band = phy_mode_to_band(phy_mode); } @@ -979,12 +1175,12 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) } } - ath10k_dbg(ATH10K_DBG_MGMT, + ath10k_dbg(ar, ATH10K_DBG_MGMT, "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); - ath10k_dbg(ATH10K_DBG_MGMT, + ath10k_dbg(ar, ATH10K_DBG_MGMT, "event mgmt rx freq %d band %d snr %d, rate_idx %d\n", status->freq, status->band, status->signal, status->rate_idx); @@ -1034,21 +1230,26 @@ static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) rx_clear_count = __le32_to_cpu(ev->rx_clear_count); cycle_count = __le32_to_cpu(ev->cycle_count); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n", err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count); spin_lock_bh(&ar->data_lock); - if (!ar->scan.in_progress) { - ath10k_warn("chan info event without a scan request?\n"); + switch (ar->scan.state) { + case ATH10K_SCAN_IDLE: + case ATH10K_SCAN_STARTING: + ath10k_warn(ar, "received chan info event without a scan request, ignoring\n"); goto exit; + case ATH10K_SCAN_RUNNING: + case ATH10K_SCAN_ABORTING: + break; } idx = freq_to_idx(ar, freq); if (idx >= ARRAY_SIZE(ar->survey)) { - ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n", + ath10k_warn(ar, "chan info: invalid frequency %d (idx %d out of bounds)\n", freq, idx); goto exit; } @@ -1079,12 +1280,12 @@ static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); } static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", skb->len); trace_ath10k_wmi_dbglog(skb->data, skb->len); @@ -1097,7 +1298,7 @@ static void ath10k_wmi_event_update_stats(struct ath10k *ar, { struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data; - ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); ath10k_debug_read_target_stats(ar, ev); } @@ -1107,7 +1308,7 @@ static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, { struct wmi_vdev_start_response_event *ev; - ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); ev = (struct wmi_vdev_start_response_event *)skb->data; @@ -1120,7 +1321,7 @@ static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n"); complete(&ar->vdev_setup_done); } @@ -1132,14 +1333,14 @@ static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, ev = (struct wmi_peer_sta_kickout_event *)skb->data; - ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n", ev->peer_macaddr.addr); rcu_read_lock(); sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL); if (!sta) { - ath10k_warn("Spurious quick kickout for STA %pM\n", + ath10k_warn(ar, "Spurious quick kickout for STA %pM\n", ev->peer_macaddr.addr); goto exit; } @@ -1216,7 +1417,7 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, (u8 *)skb_tail_pointer(bcn) - ies); if (!ie) { if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS) - ath10k_warn("no tim ie found;\n"); + ath10k_warn(ar, "no tim ie found;\n"); return; } @@ -1236,12 +1437,12 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, ie_len += expand_size; pvm_len += expand_size; } else { - ath10k_warn("tim expansion failed\n"); + ath10k_warn(ar, "tim expansion failed\n"); } } if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) { - ath10k_warn("tim pvm length is too great (%d)\n", pvm_len); + ath10k_warn(ar, "tim pvm length is too great (%d)\n", pvm_len); return; } @@ -1255,7 +1456,7 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true; } - ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", + ath10k_dbg(ar, ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", tim->dtim_count, tim->dtim_period, tim->bitmap_ctrl, pvm_len); } @@ -1333,7 +1534,7 @@ static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) return; - ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed); + ath10k_dbg(ar, ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed); if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) { new_len = ath10k_p2p_calc_noa_ie_len(noa); if (!new_len) @@ -1381,7 +1582,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ev = (struct wmi_host_swba_event *)skb->data; map = __le32_to_cpu(ev->vdev_map); - ath10k_dbg(ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n", + ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n", ev->vdev_map); for (; map; map >>= 1, vdev_id++) { @@ -1391,13 +1592,13 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) i++; if (i >= WMI_MAX_AP_VDEV) { - ath10k_warn("swba has corrupted vdev map\n"); + ath10k_warn(ar, "swba has corrupted vdev map\n"); break; } bcn_info = &ev->bcn_info[i]; - ath10k_dbg(ATH10K_DBG_MGMT, + ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n", i, __le32_to_cpu(bcn_info->tim_info.tim_len), @@ -1411,7 +1612,8 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) arvif = ath10k_get_arvif(ar, vdev_id); if (arvif == NULL) { - ath10k_warn("no vif for vdev_id %d found\n", vdev_id); + ath10k_warn(ar, "no vif for vdev_id %d found\n", + vdev_id); continue; } @@ -1428,7 +1630,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) bcn = ieee80211_beacon_get(ar->hw, arvif->vif); if (!bcn) { - ath10k_warn("could not get mac80211 beacon\n"); + ath10k_warn(ar, "could not get mac80211 beacon\n"); continue; } @@ -1440,7 +1642,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) if (arvif->beacon) { if (!arvif->beacon_sent) - ath10k_warn("SWBA overrun on vdev %d\n", + ath10k_warn(ar, "SWBA overrun on vdev %d\n", arvif->vdev_id); dma_unmap_single(arvif->ar->dev, @@ -1456,7 +1658,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ret = dma_mapping_error(arvif->ar->dev, ATH10K_SKB_CB(bcn)->paddr); if (ret) { - ath10k_warn("failed to map beacon: %d\n", ret); + ath10k_warn(ar, "failed to map beacon: %d\n", ret); dev_kfree_skb_any(bcn); goto skip; } @@ -1473,7 +1675,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); } static void ath10k_dfs_radar_report(struct ath10k *ar, @@ -1489,20 +1691,20 @@ static void ath10k_dfs_radar_report(struct ath10k *ar, reg0 = __le32_to_cpu(rr->reg0); reg1 = __le32_to_cpu(rr->reg1); - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi phyerr radar report chirp %d max_width %d agc_total_gain %d pulse_delta_diff %d\n", MS(reg0, RADAR_REPORT_REG0_PULSE_IS_CHIRP), MS(reg0, RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH), MS(reg0, RADAR_REPORT_REG0_AGC_TOTAL_GAIN), MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_DIFF)); - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi phyerr radar report pulse_delta_pean %d pulse_sidx %d fft_valid %d agc_mb_gain %d subchan_mask %d\n", MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_PEAK), MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX), MS(reg1, RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID), MS(reg1, RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN), MS(reg1, RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK)); - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi phyerr radar report pulse_tsf_offset 0x%X pulse_dur: %d\n", MS(reg1, RADAR_REPORT_REG1_PULSE_TSF_OFFSET), MS(reg1, RADAR_REPORT_REG1_PULSE_DUR)); @@ -1529,25 +1731,25 @@ static void ath10k_dfs_radar_report(struct ath10k *ar, pe.width = width; pe.rssi = rssi; - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs add pulse freq: %d, width: %d, rssi %d, tsf: %llX\n", pe.freq, pe.width, pe.rssi, pe.ts); ATH10K_DFS_STAT_INC(ar, pulses_detected); if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) { - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs no pulse pattern detected, yet\n"); return; } - ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n"); + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs radar detected\n"); ATH10K_DFS_STAT_INC(ar, radar_detected); /* Control radar events reporting in debugfs file dfs_block_radar_events */ if (ar->dfs_block_radar_events) { - ath10k_info("DFS Radar detected, but ignored as requested\n"); + ath10k_info(ar, "DFS Radar detected, but ignored as requested\n"); return; } @@ -1566,13 +1768,13 @@ static int ath10k_dfs_fft_report(struct ath10k *ar, reg1 = __le32_to_cpu(fftr->reg1); rssi = event->hdr.rssi_combined; - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n", MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB), MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB), MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX), MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX)); - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi phyerr fft report rel_pwr_db %d avgpwr_db %d peak_mag %d num_store_bin %d\n", MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB), MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB), @@ -1584,7 +1786,7 @@ static int ath10k_dfs_fft_report(struct ath10k *ar, /* false event detection */ if (rssi == DFS_RSSI_POSSIBLY_FALSE && peak_mag < 2 * DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE) { - ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs false pulse detected\n"); + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs false pulse detected\n"); ATH10K_DFS_STAT_INC(ar, pulses_discarded); return -EINVAL; } @@ -1603,7 +1805,7 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar, u8 *tlv_buf; buf_len = __le32_to_cpu(event->hdr.buf_len); - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n", event->hdr.phy_err_code, event->hdr.rssi_combined, __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len); @@ -1616,21 +1818,22 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar, while (i < buf_len) { if (i + sizeof(*tlv) > buf_len) { - ath10k_warn("too short buf for tlv header (%d)\n", i); + ath10k_warn(ar, "too short buf for tlv header (%d)\n", + i); return; } tlv = (struct phyerr_tlv *)&event->bufp[i]; tlv_len = __le16_to_cpu(tlv->len); tlv_buf = &event->bufp[i + sizeof(*tlv)]; - ath10k_dbg(ATH10K_DBG_REGULATORY, + ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n", tlv_len, tlv->tag, tlv->sig); switch (tlv->tag) { case PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY: if (i + sizeof(*tlv) + sizeof(*rr) > buf_len) { - ath10k_warn("too short radar pulse summary (%d)\n", + ath10k_warn(ar, "too short radar pulse summary (%d)\n", i); return; } @@ -1640,7 +1843,8 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar, break; case PHYERR_TLV_TAG_SEARCH_FFT_REPORT: if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) { - ath10k_warn("too short fft report (%d)\n", i); + ath10k_warn(ar, "too short fft report (%d)\n", + i); return; } @@ -1659,7 +1863,54 @@ static void ath10k_wmi_event_spectral_scan(struct ath10k *ar, struct wmi_single_phyerr_rx_event *event, u64 tsf) { - ath10k_dbg(ATH10K_DBG_WMI, "wmi event spectral scan\n"); + int buf_len, tlv_len, res, i = 0; + struct phyerr_tlv *tlv; + u8 *tlv_buf; + struct phyerr_fft_report *fftr; + size_t fftr_len; + + buf_len = __le32_to_cpu(event->hdr.buf_len); + + while (i < buf_len) { + if (i + sizeof(*tlv) > buf_len) { + ath10k_warn(ar, "failed to parse phyerr tlv header at byte %d\n", + i); + return; + } + + tlv = (struct phyerr_tlv *)&event->bufp[i]; + tlv_len = __le16_to_cpu(tlv->len); + tlv_buf = &event->bufp[i + sizeof(*tlv)]; + + if (i + sizeof(*tlv) + tlv_len > buf_len) { + ath10k_warn(ar, "failed to parse phyerr tlv payload at byte %d\n", + i); + return; + } + + switch (tlv->tag) { + case PHYERR_TLV_TAG_SEARCH_FFT_REPORT: + if (sizeof(*fftr) > tlv_len) { + ath10k_warn(ar, "failed to parse fft report at byte %d\n", + i); + return; + } + + fftr_len = tlv_len - sizeof(*fftr); + fftr = (struct phyerr_fft_report *)tlv_buf; + res = ath10k_spectral_process_fft(ar, event, + fftr, fftr_len, + tsf); + if (res < 0) { + ath10k_warn(ar, "failed to process fft report: %d\n", + res); + return; + } + break; + } + + i += sizeof(*tlv) + tlv_len; + } } static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) @@ -1674,7 +1925,7 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) /* Check if combined event available */ if (left_len < sizeof(*comb_event)) { - ath10k_warn("wmi phyerr combined event wrong len\n"); + ath10k_warn(ar, "wmi phyerr combined event wrong len\n"); return; } @@ -1688,7 +1939,7 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) tsf <<= 32; tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event phyerr count %d tsf64 0x%llX\n", count, tsf); @@ -1696,7 +1947,8 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) for (i = 0; i < count; i++) { /* Check if we can read event header */ if (left_len < sizeof(*event)) { - ath10k_warn("single event (%d) wrong head len\n", i); + ath10k_warn(ar, "single event (%d) wrong head len\n", + i); return; } @@ -1706,7 +1958,7 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) phy_err_code = event->hdr.phy_err_code; if (left_len < buf_len) { - ath10k_warn("single event (%d) wrong buf len\n", i); + ath10k_warn(ar, "single event (%d) wrong buf len\n", i); return; } @@ -1733,13 +1985,13 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n"); } static void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n"); } static void ath10k_wmi_event_debug_print(struct ath10k *ar, @@ -1764,7 +2016,7 @@ static void ath10k_wmi_event_debug_print(struct ath10k *ar, } if (i == sizeof(buf) - 1) - ath10k_warn("wmi debug print truncated: %d\n", skb->len); + ath10k_warn(ar, "wmi debug print truncated: %d\n", skb->len); /* for some reason the debug prints end with \n, remove that */ if (skb->data[i - 1] == '\n') @@ -1773,108 +2025,108 @@ static void ath10k_wmi_event_debug_print(struct ath10k *ar, /* the last byte is always reserved for the null character */ buf[i] = '\0'; - ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf); } static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n"); } static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n"); } static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n"); } static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n"); } static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n"); } static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); } static void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); } static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); } static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); } static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n"); } static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n"); } static void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n"); } static void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n"); } static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); } static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); } static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); } static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); } static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, @@ -1894,7 +2146,7 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, &paddr, GFP_ATOMIC); if (!ar->wmi.mem_chunks[idx].vaddr) { - ath10k_warn("failed to allocate memory chunk\n"); + ath10k_warn(ar, "failed to allocate memory chunk\n"); return -ENOMEM; } @@ -1912,9 +2164,10 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_service_ready_event *ev = (void *)skb->data; + DECLARE_BITMAP(svc_bmap, WMI_SERVICE_BM_SIZE) = {}; if (skb->len < sizeof(*ev)) { - ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + ath10k_warn(ar, "Service ready event was %d B but expected %zu B. Wrong firmware version?\n", skb->len, sizeof(*ev)); return; } @@ -1937,7 +2190,7 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features); if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { - ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", + ath10k_warn(ar, "hardware advertises support for more spatial streams than it should (%d > %d)\n", ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM; } @@ -1945,8 +2198,10 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, ar->ath_common.regulatory.current_rd = __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); - ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, - sizeof(ev->wmi_service_bitmap)); + wmi_main_svc_map(ev->wmi_service_bitmap, svc_bmap); + ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap)); + ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ", + ev->wmi_service_bitmap, sizeof(ev->wmi_service_bitmap)); if (strlen(ar->hw->wiphy->fw_version) == 0) { snprintf(ar->hw->wiphy->fw_version, @@ -1960,11 +2215,11 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, /* FIXME: it probably should be better to support this */ if (__le32_to_cpu(ev->num_mem_reqs) > 0) { - ath10k_warn("target requested %d memory chunks; ignoring\n", + ath10k_warn(ar, "target requested %d memory chunks; ignoring\n", __le32_to_cpu(ev->num_mem_reqs)); } - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", __le32_to_cpu(ev->sw_version), __le32_to_cpu(ev->sw_version_1), @@ -1986,9 +2241,10 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; int ret; struct wmi_service_ready_event_10x *ev = (void *)skb->data; + DECLARE_BITMAP(svc_bmap, WMI_SERVICE_BM_SIZE) = {}; if (skb->len < sizeof(*ev)) { - ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + ath10k_warn(ar, "Service ready event was %d B but expected %zu B. Wrong firmware version?\n", skb->len, sizeof(*ev)); return; } @@ -2004,7 +2260,7 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { - ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", + ath10k_warn(ar, "hardware advertises support for more spatial streams than it should (%d > %d)\n", ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM; } @@ -2012,8 +2268,10 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, ar->ath_common.regulatory.current_rd = __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); - ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, - sizeof(ev->wmi_service_bitmap)); + wmi_10x_svc_map(ev->wmi_service_bitmap, svc_bmap); + ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap)); + ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ", + ev->wmi_service_bitmap, sizeof(ev->wmi_service_bitmap)); if (strlen(ar->hw->wiphy->fw_version) == 0) { snprintf(ar->hw->wiphy->fw_version, @@ -2026,7 +2284,7 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs); if (num_mem_reqs > ATH10K_MAX_MEM_REQS) { - ath10k_warn("requested memory chunks number (%d) exceeds the limit\n", + ath10k_warn(ar, "requested memory chunks number (%d) exceeds the limit\n", num_mem_reqs); return; } @@ -2034,7 +2292,7 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, if (!num_mem_reqs) goto exit; - ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n", num_mem_reqs); for (i = 0; i < num_mem_reqs; ++i) { @@ -2052,7 +2310,7 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) num_units = TARGET_10X_NUM_VDEVS + 1; - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n", req_id, __le32_to_cpu(ev->mem_reqs[i].num_units), @@ -2067,7 +2325,7 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, } exit: - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", __le32_to_cpu(ev->sw_version), __le32_to_cpu(ev->abi_version), @@ -2091,7 +2349,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n", __le32_to_cpu(ev->sw_version), __le32_to_cpu(ev->abi_version), @@ -2211,7 +2469,7 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_ready_event_rx(ar, skb); break; default: - ath10k_warn("Unknown eventid: %d\n", id); + ath10k_warn(ar, "Unknown eventid: %d\n", id); break; } @@ -2318,27 +2576,151 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_ready_event_rx(ar, skb); break; default: - ath10k_warn("Unknown eventid: %d\n", id); + ath10k_warn(ar, "Unknown eventid: %d\n", id); break; } dev_kfree_skb(skb); } +static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_10_2_event_id id; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; + + trace_ath10k_wmi_event(id, skb->data, skb->len); + + switch (id) { + case WMI_10_2_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ + return; + case WMI_10_2_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_10_2_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_10_2_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_10_2_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_10_2_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_10_2_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_10_2_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_10_2_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_10_2_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_10_2_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_10_2_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_10_2_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_10_2_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_10_2_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_10_2_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_10_2_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_10_2_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_10_2_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_10_2_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_10_2_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_10_2_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_10_2_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_10_2_INST_RSSI_STATS_EVENTID: + ath10k_wmi_event_inst_rssi_stats(ar, skb); + break; + case WMI_10_2_VDEV_STANDBY_REQ_EVENTID: + ath10k_wmi_event_vdev_standby_req(ar, skb); + break; + case WMI_10_2_VDEV_RESUME_REQ_EVENTID: + ath10k_wmi_event_vdev_resume_req(ar, skb); + break; + case WMI_10_2_SERVICE_READY_EVENTID: + ath10k_wmi_10x_service_ready_event_rx(ar, skb); + break; + case WMI_10_2_READY_EVENTID: + ath10k_wmi_ready_event_rx(ar, skb); + break; + case WMI_10_2_RTT_KEEPALIVE_EVENTID: + case WMI_10_2_GPIO_INPUT_EVENTID: + case WMI_10_2_PEER_RATECODE_LIST_EVENTID: + case WMI_10_2_GENERIC_BUFFER_EVENTID: + case WMI_10_2_MCAST_BUF_RELEASE_EVENTID: + case WMI_10_2_MCAST_LIST_AGEOUT_EVENTID: + case WMI_10_2_WDS_PEER_EVENTID: + ath10k_dbg(ar, ATH10K_DBG_WMI, + "received event id %d not implemented\n", id); + break; + default: + ath10k_warn(ar, "Unknown eventid: %d\n", id); + break; + } + + dev_kfree_skb(skb); +} static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - ath10k_wmi_10x_process_rx(ar, skb); - else + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + ath10k_wmi_10_2_process_rx(ar, skb); + else + ath10k_wmi_10x_process_rx(ar, skb); + } else { ath10k_wmi_main_process_rx(ar, skb); + } } /* WMI Initialization functions */ int ath10k_wmi_attach(struct ath10k *ar) { if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ar->wmi.cmd = &wmi_10x_cmd_map; + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + ar->wmi.cmd = &wmi_10_2_cmd_map; + else + ar->wmi.cmd = &wmi_10x_cmd_map; + ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ar->wmi.pdev_param = &wmi_10x_pdev_param_map; } else { @@ -2388,7 +2770,7 @@ int ath10k_wmi_connect(struct ath10k *ar) status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp); if (status) { - ath10k_warn("failed to connect to WMI CONTROL service status: %d\n", + ath10k_warn(ar, "failed to connect to WMI CONTROL service status: %d\n", status); return status; } @@ -2404,7 +2786,7 @@ static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, struct wmi_pdev_set_regdomain_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2415,7 +2797,7 @@ static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g); cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", rd, rd2g, rd5g, ctl2g, ctl5g); @@ -2431,7 +2813,7 @@ static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, struct wmi_pdev_set_regdomain_cmd_10x *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2443,7 +2825,7 @@ static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g); cmd->dfs_domain = __cpu_to_le32(dfs_reg); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n", rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg); @@ -2473,7 +2855,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, if (arg->passive) return -EINVAL; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2491,7 +2873,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, cmd->chan.reg_classid = arg->reg_class_id; cmd->chan.antenna_max = arg->max_antenna_gain; - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi set channel mode %d freq %d\n", arg->mode, arg->freq); @@ -2504,7 +2886,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) struct wmi_pdev_suspend_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2518,7 +2900,7 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar) { struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(0); + skb = ath10k_wmi_alloc_skb(ar, 0); if (skb == NULL) return -ENOMEM; @@ -2531,11 +2913,12 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) struct sk_buff *skb; if (id == WMI_PDEV_PARAM_UNSUPPORTED) { - ath10k_warn("pdev param %d not supported by firmware\n", id); + ath10k_warn(ar, "pdev param %d not supported by firmware\n", + id); return -EOPNOTSUPP; } - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2543,7 +2926,7 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) cmd->param_id = __cpu_to_le32(id); cmd->param_value = __cpu_to_le32(value); - ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", id, value); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); } @@ -2610,7 +2993,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) len = sizeof(*cmd) + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); - buf = ath10k_wmi_alloc_skb(len); + buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) return -ENOMEM; @@ -2621,7 +3004,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) goto out; } - ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", ar->wmi.num_mem_chunks); cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); @@ -2634,7 +3017,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) cmd->host_mem_chunks[i].req_id = __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi chunk %d len %d requested, addr 0x%llx\n", i, ar->wmi.mem_chunks[i].len, @@ -2643,7 +3026,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) out: memcpy(&cmd->resource_config, &config, sizeof(config)); - ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n"); return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); } @@ -2701,7 +3084,7 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) len = sizeof(*cmd) + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); - buf = ath10k_wmi_alloc_skb(len); + buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) return -ENOMEM; @@ -2712,7 +3095,7 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) goto out; } - ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", ar->wmi.num_mem_chunks); cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); @@ -2725,7 +3108,7 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) cmd->host_mem_chunks[i].req_id = __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi chunk %d len %d requested, addr 0x%llx\n", i, ar->wmi.mem_chunks[i].len, @@ -2734,7 +3117,98 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) out: memcpy(&cmd->resource_config, &config, sizeof(config)); - ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n"); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); +} + +static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) +{ + struct wmi_init_cmd_10_2 *cmd; + struct sk_buff *buf; + struct wmi_resource_config_10x config = {}; + u32 len, val; + int i; + + config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); + config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE); + + config.scan_max_pending_reqs = + __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS); + + config.bmiss_offload_max_vdev = + __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_vdev = + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_ap_profiles = + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES); + + config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS); + config.num_mcast_table_elems = + __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS); + + config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM); + + val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); + + config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG); + + config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES); + + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(ar, len); + if (!buf) + return -ENOMEM; + + cmd = (struct wmi_init_cmd_10_2 *)buf->data; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + ar->wmi.num_mem_chunks); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%llx\n", + i, + ar->wmi.mem_chunks[i].len, + (unsigned long long)ar->wmi.mem_chunks[i].paddr); + } +out: + memcpy(&cmd->resource_config.common, &config, sizeof(config)); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n"); return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); } @@ -2742,10 +3216,14 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) { int ret; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - ret = ath10k_wmi_10x_cmd_init(ar); - else + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + ret = ath10k_wmi_10_2_cmd_init(ar); + else + ret = ath10k_wmi_10x_cmd_init(ar); + } else { ret = ath10k_wmi_main_cmd_init(ar); + } return ret; } @@ -2822,7 +3300,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, if (len < 0) return len; /* len contains error code here */ - skb = ath10k_wmi_alloc_skb(len); + skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) return -ENOMEM; @@ -2865,8 +3343,8 @@ int ath10k_wmi_start_scan(struct ath10k *ar, channels->num_chan = __cpu_to_le32(arg->n_channels); for (i = 0; i < arg->n_channels; i++) - channels->channel_list[i] = - __cpu_to_le32(arg->channels[i]); + channels->channel_list[i].freq = + __cpu_to_le16(arg->channels[i]); off += sizeof(*channels); off += sizeof(__le32) * arg->n_channels; @@ -2918,7 +3396,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, return -EINVAL; } - ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n"); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); } @@ -2960,7 +3438,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) return -EINVAL; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -2976,7 +3454,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) cmd->scan_id = __cpu_to_le32(scan_id); cmd->scan_req_id = __cpu_to_le32(req_id); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", arg->req_id, arg->req_type, arg->u.scan_id); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); @@ -2990,7 +3468,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, struct wmi_vdev_create_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3000,7 +3478,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, cmd->vdev_subtype = __cpu_to_le32(subtype); memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", vdev_id, type, subtype, macaddr); @@ -3012,14 +3490,14 @@ int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) struct wmi_vdev_delete_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_vdev_delete_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); @@ -3052,7 +3530,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, else return -EINVAL; /* should not happen, we already check cmd_id */ - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3090,7 +3568,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, cmd->chan.reg_classid = arg->channel.reg_class_id; cmd->chan.antenna_max = arg->channel.max_antenna_gain; - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, " "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id, flags, arg->channel.freq, arg->channel.mode, @@ -3120,14 +3598,14 @@ int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) struct wmi_vdev_stop_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_vdev_stop_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); - ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); } @@ -3137,7 +3615,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) struct wmi_vdev_up_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3146,7 +3624,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) cmd->vdev_assoc_id = __cpu_to_le32(aid); memcpy(&cmd->vdev_bssid.addr, bssid, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", vdev_id, aid, bssid); @@ -3158,14 +3636,14 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) struct wmi_vdev_down_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_vdev_down_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt vdev down id 0x%x\n", vdev_id); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); @@ -3178,13 +3656,13 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, struct sk_buff *skb; if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) { - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "vdev param %d not supported by firmware\n", param_id); return -EOPNOTSUPP; } - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3193,7 +3671,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, cmd->param_id = __cpu_to_le32(param_id); cmd->param_value = __cpu_to_le32(param_value); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev id 0x%x set param %d value %d\n", vdev_id, param_id, param_value); @@ -3211,7 +3689,7 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) return -EINVAL; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + arg->key_len); if (!skb) return -ENOMEM; @@ -3229,20 +3707,76 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, if (arg->key_data) memcpy(cmd->key_data, arg->key_data, arg->key_len); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev install key idx %d cipher %d len %d\n", arg->key_idx, arg->key_cipher, arg->key_len); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_install_key_cmdid); } +int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg) +{ + struct wmi_vdev_spectral_conf_cmd *cmd; + struct sk_buff *skb; + u32 cmdid; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_spectral_conf_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->scan_count = __cpu_to_le32(arg->scan_count); + cmd->scan_period = __cpu_to_le32(arg->scan_period); + cmd->scan_priority = __cpu_to_le32(arg->scan_priority); + cmd->scan_fft_size = __cpu_to_le32(arg->scan_fft_size); + cmd->scan_gc_ena = __cpu_to_le32(arg->scan_gc_ena); + cmd->scan_restart_ena = __cpu_to_le32(arg->scan_restart_ena); + cmd->scan_noise_floor_ref = __cpu_to_le32(arg->scan_noise_floor_ref); + cmd->scan_init_delay = __cpu_to_le32(arg->scan_init_delay); + cmd->scan_nb_tone_thr = __cpu_to_le32(arg->scan_nb_tone_thr); + cmd->scan_str_bin_thr = __cpu_to_le32(arg->scan_str_bin_thr); + cmd->scan_wb_rpt_mode = __cpu_to_le32(arg->scan_wb_rpt_mode); + cmd->scan_rssi_rpt_mode = __cpu_to_le32(arg->scan_rssi_rpt_mode); + cmd->scan_rssi_thr = __cpu_to_le32(arg->scan_rssi_thr); + cmd->scan_pwr_format = __cpu_to_le32(arg->scan_pwr_format); + cmd->scan_rpt_mode = __cpu_to_le32(arg->scan_rpt_mode); + cmd->scan_bin_scale = __cpu_to_le32(arg->scan_bin_scale); + cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj); + cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask); + + cmdid = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmdid); +} + +int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, + u32 enable) +{ + struct wmi_vdev_spectral_enable_cmd *cmd; + struct sk_buff *skb; + u32 cmdid; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_spectral_enable_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->trigger_cmd = __cpu_to_le32(trigger); + cmd->enable_cmd = __cpu_to_le32(enable); + + cmdid = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmdid); +} + int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, const u8 peer_addr[ETH_ALEN]) { struct wmi_peer_create_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3250,7 +3784,7 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, cmd->vdev_id = __cpu_to_le32(vdev_id); memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); @@ -3262,7 +3796,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, struct wmi_peer_delete_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3270,7 +3804,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, cmd->vdev_id = __cpu_to_le32(vdev_id); memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer delete vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); @@ -3282,7 +3816,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, struct wmi_peer_flush_tids_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3291,7 +3825,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap); memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", vdev_id, peer_addr, tid_bitmap); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); @@ -3304,7 +3838,7 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, struct wmi_peer_set_param_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3314,7 +3848,7 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, cmd->param_value = __cpu_to_le32(param_value); memcpy(&cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev %d peer 0x%pM set param %d value %d\n", vdev_id, peer_addr, param_id, param_value); @@ -3327,7 +3861,7 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, struct wmi_sta_powersave_mode_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3335,7 +3869,7 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->sta_ps_mode = __cpu_to_le32(psmode); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi set powersave id 0x%x mode %d\n", vdev_id, psmode); @@ -3350,7 +3884,7 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, struct wmi_sta_powersave_param_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3359,7 +3893,7 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, cmd->param_id = __cpu_to_le32(param_id); cmd->param_value = __cpu_to_le32(value); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sta ps param vdev_id 0x%x param %d value %d\n", vdev_id, param_id, value); return ath10k_wmi_cmd_send(ar, skb, @@ -3375,7 +3909,7 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, if (!mac) return -EINVAL; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3385,7 +3919,7 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, cmd->param_value = __cpu_to_le32(value); memcpy(&cmd->peer_macaddr, mac, ETH_ALEN); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", vdev_id, param_id, value, mac); @@ -3405,7 +3939,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel); - skb = ath10k_wmi_alloc_skb(len); + skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) return -EINVAL; @@ -3447,24 +3981,12 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); } -int ath10k_wmi_peer_assoc(struct ath10k *ar, - const struct wmi_peer_assoc_complete_arg *arg) +static void +ath10k_wmi_peer_assoc_fill(struct ath10k *ar, void *buf, + const struct wmi_peer_assoc_complete_arg *arg) { - struct wmi_peer_assoc_complete_cmd *cmd; - struct sk_buff *skb; + struct wmi_common_peer_assoc_complete_cmd *cmd = buf; - if (arg->peer_mpdu_density > 16) - return -EINVAL; - if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) - return -EINVAL; - if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) - return -EINVAL; - - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); - if (!skb) - return -ENOMEM; - - cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); cmd->peer_new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1); cmd->peer_associd = __cpu_to_le32(arg->peer_aid); @@ -3499,8 +4021,80 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, __cpu_to_le32(arg->peer_vht_rates.tx_max_rate); cmd->peer_vht_rates.tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); +} - ath10k_dbg(ATH10K_DBG_WMI, +static void +ath10k_wmi_peer_assoc_fill_main(struct ath10k *ar, void *buf, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_main_peer_assoc_complete_cmd *cmd = buf; + + ath10k_wmi_peer_assoc_fill(ar, buf, arg); + memset(cmd->peer_ht_info, 0, sizeof(cmd->peer_ht_info)); +} + +static void +ath10k_wmi_peer_assoc_fill_10_1(struct ath10k *ar, void *buf, + const struct wmi_peer_assoc_complete_arg *arg) +{ + ath10k_wmi_peer_assoc_fill(ar, buf, arg); +} + +static void +ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_10_2_peer_assoc_complete_cmd *cmd = buf; + int max_mcs, max_nss; + u32 info0; + + /* TODO: Is using max values okay with firmware? */ + max_mcs = 0xf; + max_nss = 0xf; + + info0 = SM(max_mcs, WMI_PEER_ASSOC_INFO0_MAX_MCS_IDX) | + SM(max_nss, WMI_PEER_ASSOC_INFO0_MAX_NSS); + + ath10k_wmi_peer_assoc_fill(ar, buf, arg); + cmd->info0 = __cpu_to_le32(info0); +} + +int ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct sk_buff *skb; + int len; + + if (arg->peer_mpdu_density > 16) + return -EINVAL; + if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) + return -EINVAL; + if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) + return -EINVAL; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd); + else + len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd); + } else { + len = sizeof(struct wmi_main_peer_assoc_complete_cmd); + } + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return -ENOMEM; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) + ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg); + else + ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg); + } else { + ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg); + } + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer assoc vdev %d addr %pM (%s)\n", arg->vdev_id, arg->addr, arg->peer_reassoc ? "reassociate" : "new"); @@ -3518,7 +4112,7 @@ int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) int ret; u16 fc; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3532,6 +4126,7 @@ int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) cmd->msdu_id = 0; cmd->frame_control = __cpu_to_le32(fc); cmd->flags = 0; + cmd->antenna_mask = __cpu_to_le32(WMI_BCN_TX_REF_DEF_ANTENNA); if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero) cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO); @@ -3565,7 +4160,7 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, struct wmi_pdev_set_wmm_params *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3575,7 +4170,7 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi); ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); - ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_wmm_params_cmdid); } @@ -3585,14 +4180,14 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) struct wmi_request_stats_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; cmd = (struct wmi_request_stats_cmd *)skb->data; cmd->stats_id = __cpu_to_le32(stats_id); - ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); } @@ -3602,7 +4197,7 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, struct wmi_force_fw_hang_cmd *cmd; struct sk_buff *skb; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3610,7 +4205,7 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, cmd->type = __cpu_to_le32(type); cmd->delay_ms = __cpu_to_le32(delay_ms); - ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", type, delay_ms); return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); } @@ -3621,7 +4216,7 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) struct sk_buff *skb; u32 cfg; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -3642,7 +4237,7 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) cmd->config_enable = __cpu_to_le32(cfg); cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK); - ath10k_dbg(ATH10K_DBG_WMI, + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi dbglog cfg modules %08x %08x config %08x %08x\n", __le32_to_cpu(cmd->module_enable), __le32_to_cpu(cmd->module_valid), diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index e93df2c10413..e70836586756 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -73,116 +73,279 @@ struct wmi_cmd_hdr { #define HTC_PROTOCOL_VERSION 0x0002 #define WMI_PROTOCOL_VERSION 0x0002 -enum wmi_service_id { - WMI_SERVICE_BEACON_OFFLOAD = 0, /* beacon offload */ - WMI_SERVICE_SCAN_OFFLOAD, /* scan offload */ - WMI_SERVICE_ROAM_OFFLOAD, /* roam offload */ - WMI_SERVICE_BCN_MISS_OFFLOAD, /* beacon miss offload */ - WMI_SERVICE_STA_PWRSAVE, /* fake sleep + basic power save */ - WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */ - WMI_SERVICE_AP_UAPSD, /* uapsd on AP */ - WMI_SERVICE_AP_DFS, /* DFS on AP */ - WMI_SERVICE_11AC, /* supports 11ac */ - WMI_SERVICE_BLOCKACK, /* Supports triggering ADDBA/DELBA from host*/ - WMI_SERVICE_PHYERR, /* PHY error */ - WMI_SERVICE_BCN_FILTER, /* Beacon filter support */ - WMI_SERVICE_RTT, /* RTT (round trip time) support */ - WMI_SERVICE_RATECTRL, /* Rate-control */ - WMI_SERVICE_WOW, /* WOW Support */ - WMI_SERVICE_RATECTRL_CACHE, /* Rate-control caching */ - WMI_SERVICE_IRAM_TIDS, /* TIDs in IRAM */ - WMI_SERVICE_ARPNS_OFFLOAD, /* ARP NS Offload support */ - WMI_SERVICE_NLO, /* Network list offload service */ - WMI_SERVICE_GTK_OFFLOAD, /* GTK offload */ - WMI_SERVICE_SCAN_SCH, /* Scan Scheduler Service */ - WMI_SERVICE_CSA_OFFLOAD, /* CSA offload service */ - WMI_SERVICE_CHATTER, /* Chatter service */ - WMI_SERVICE_COEX_FREQAVOID, /* FW report freq range to avoid */ - WMI_SERVICE_PACKET_POWER_SAVE, /* packet power save service */ - WMI_SERVICE_FORCE_FW_HANG, /* To test fw recovery mechanism */ - WMI_SERVICE_GPIO, /* GPIO service */ - WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */ - WMI_STA_UAPSD_BASIC_AUTO_TRIG, /* UAPSD AC Trigger Generation */ - WMI_STA_UAPSD_VAR_AUTO_TRIG, /* -do- */ - WMI_SERVICE_STA_KEEP_ALIVE, /* STA keep alive mechanism support */ - WMI_SERVICE_TX_ENCAP, /* Packet type for TX encapsulation */ +enum wmi_service { + WMI_SERVICE_BEACON_OFFLOAD = 0, + WMI_SERVICE_SCAN_OFFLOAD, + WMI_SERVICE_ROAM_OFFLOAD, + WMI_SERVICE_BCN_MISS_OFFLOAD, + WMI_SERVICE_STA_PWRSAVE, + WMI_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_SERVICE_AP_UAPSD, + WMI_SERVICE_AP_DFS, + WMI_SERVICE_11AC, + WMI_SERVICE_BLOCKACK, + WMI_SERVICE_PHYERR, + WMI_SERVICE_BCN_FILTER, + WMI_SERVICE_RTT, + WMI_SERVICE_RATECTRL, + WMI_SERVICE_WOW, + WMI_SERVICE_RATECTRL_CACHE, + WMI_SERVICE_IRAM_TIDS, + WMI_SERVICE_ARPNS_OFFLOAD, + WMI_SERVICE_NLO, + WMI_SERVICE_GTK_OFFLOAD, + WMI_SERVICE_SCAN_SCH, + WMI_SERVICE_CSA_OFFLOAD, + WMI_SERVICE_CHATTER, + WMI_SERVICE_COEX_FREQAVOID, + WMI_SERVICE_PACKET_POWER_SAVE, + WMI_SERVICE_FORCE_FW_HANG, + WMI_SERVICE_GPIO, + WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_SERVICE_STA_KEEP_ALIVE, + WMI_SERVICE_TX_ENCAP, + WMI_SERVICE_BURST, + WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, + WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, +}; - WMI_SERVICE_LAST, - WMI_MAX_SERVICE = 64 /* max service */ +enum wmi_10x_service { + WMI_10X_SERVICE_BEACON_OFFLOAD = 0, + WMI_10X_SERVICE_SCAN_OFFLOAD, + WMI_10X_SERVICE_ROAM_OFFLOAD, + WMI_10X_SERVICE_BCN_MISS_OFFLOAD, + WMI_10X_SERVICE_STA_PWRSAVE, + WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_10X_SERVICE_AP_UAPSD, + WMI_10X_SERVICE_AP_DFS, + WMI_10X_SERVICE_11AC, + WMI_10X_SERVICE_BLOCKACK, + WMI_10X_SERVICE_PHYERR, + WMI_10X_SERVICE_BCN_FILTER, + WMI_10X_SERVICE_RTT, + WMI_10X_SERVICE_RATECTRL, + WMI_10X_SERVICE_WOW, + WMI_10X_SERVICE_RATECTRL_CACHE, + WMI_10X_SERVICE_IRAM_TIDS, + WMI_10X_SERVICE_BURST, + + /* introduced in 10.2 */ + WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT, + WMI_10X_SERVICE_FORCE_FW_HANG, + WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT, +}; + +enum wmi_main_service { + WMI_MAIN_SERVICE_BEACON_OFFLOAD = 0, + WMI_MAIN_SERVICE_SCAN_OFFLOAD, + WMI_MAIN_SERVICE_ROAM_OFFLOAD, + WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD, + WMI_MAIN_SERVICE_STA_PWRSAVE, + WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_MAIN_SERVICE_AP_UAPSD, + WMI_MAIN_SERVICE_AP_DFS, + WMI_MAIN_SERVICE_11AC, + WMI_MAIN_SERVICE_BLOCKACK, + WMI_MAIN_SERVICE_PHYERR, + WMI_MAIN_SERVICE_BCN_FILTER, + WMI_MAIN_SERVICE_RTT, + WMI_MAIN_SERVICE_RATECTRL, + WMI_MAIN_SERVICE_WOW, + WMI_MAIN_SERVICE_RATECTRL_CACHE, + WMI_MAIN_SERVICE_IRAM_TIDS, + WMI_MAIN_SERVICE_ARPNS_OFFLOAD, + WMI_MAIN_SERVICE_NLO, + WMI_MAIN_SERVICE_GTK_OFFLOAD, + WMI_MAIN_SERVICE_SCAN_SCH, + WMI_MAIN_SERVICE_CSA_OFFLOAD, + WMI_MAIN_SERVICE_CHATTER, + WMI_MAIN_SERVICE_COEX_FREQAVOID, + WMI_MAIN_SERVICE_PACKET_POWER_SAVE, + WMI_MAIN_SERVICE_FORCE_FW_HANG, + WMI_MAIN_SERVICE_GPIO, + WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_MAIN_SERVICE_STA_KEEP_ALIVE, + WMI_MAIN_SERVICE_TX_ENCAP, }; static inline char *wmi_service_name(int service_id) { +#define SVCSTR(x) case x: return #x + switch (service_id) { - case WMI_SERVICE_BEACON_OFFLOAD: - return "BEACON_OFFLOAD"; - case WMI_SERVICE_SCAN_OFFLOAD: - return "SCAN_OFFLOAD"; - case WMI_SERVICE_ROAM_OFFLOAD: - return "ROAM_OFFLOAD"; - case WMI_SERVICE_BCN_MISS_OFFLOAD: - return "BCN_MISS_OFFLOAD"; - case WMI_SERVICE_STA_PWRSAVE: - return "STA_PWRSAVE"; - case WMI_SERVICE_STA_ADVANCED_PWRSAVE: - return "STA_ADVANCED_PWRSAVE"; - case WMI_SERVICE_AP_UAPSD: - return "AP_UAPSD"; - case WMI_SERVICE_AP_DFS: - return "AP_DFS"; - case WMI_SERVICE_11AC: - return "11AC"; - case WMI_SERVICE_BLOCKACK: - return "BLOCKACK"; - case WMI_SERVICE_PHYERR: - return "PHYERR"; - case WMI_SERVICE_BCN_FILTER: - return "BCN_FILTER"; - case WMI_SERVICE_RTT: - return "RTT"; - case WMI_SERVICE_RATECTRL: - return "RATECTRL"; - case WMI_SERVICE_WOW: - return "WOW"; - case WMI_SERVICE_RATECTRL_CACHE: - return "RATECTRL CACHE"; - case WMI_SERVICE_IRAM_TIDS: - return "IRAM TIDS"; - case WMI_SERVICE_ARPNS_OFFLOAD: - return "ARPNS_OFFLOAD"; - case WMI_SERVICE_NLO: - return "NLO"; - case WMI_SERVICE_GTK_OFFLOAD: - return "GTK_OFFLOAD"; - case WMI_SERVICE_SCAN_SCH: - return "SCAN_SCH"; - case WMI_SERVICE_CSA_OFFLOAD: - return "CSA_OFFLOAD"; - case WMI_SERVICE_CHATTER: - return "CHATTER"; - case WMI_SERVICE_COEX_FREQAVOID: - return "COEX_FREQAVOID"; - case WMI_SERVICE_PACKET_POWER_SAVE: - return "PACKET_POWER_SAVE"; - case WMI_SERVICE_FORCE_FW_HANG: - return "FORCE FW HANG"; - case WMI_SERVICE_GPIO: - return "GPIO"; - case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM: - return "MODULATED DTIM"; - case WMI_STA_UAPSD_BASIC_AUTO_TRIG: - return "BASIC UAPSD"; - case WMI_STA_UAPSD_VAR_AUTO_TRIG: - return "VAR UAPSD"; - case WMI_SERVICE_STA_KEEP_ALIVE: - return "STA KEEP ALIVE"; - case WMI_SERVICE_TX_ENCAP: - return "TX ENCAP"; + SVCSTR(WMI_SERVICE_BEACON_OFFLOAD); + SVCSTR(WMI_SERVICE_SCAN_OFFLOAD); + SVCSTR(WMI_SERVICE_ROAM_OFFLOAD); + SVCSTR(WMI_SERVICE_BCN_MISS_OFFLOAD); + SVCSTR(WMI_SERVICE_STA_PWRSAVE); + SVCSTR(WMI_SERVICE_STA_ADVANCED_PWRSAVE); + SVCSTR(WMI_SERVICE_AP_UAPSD); + SVCSTR(WMI_SERVICE_AP_DFS); + SVCSTR(WMI_SERVICE_11AC); + SVCSTR(WMI_SERVICE_BLOCKACK); + SVCSTR(WMI_SERVICE_PHYERR); + SVCSTR(WMI_SERVICE_BCN_FILTER); + SVCSTR(WMI_SERVICE_RTT); + SVCSTR(WMI_SERVICE_RATECTRL); + SVCSTR(WMI_SERVICE_WOW); + SVCSTR(WMI_SERVICE_RATECTRL_CACHE); + SVCSTR(WMI_SERVICE_IRAM_TIDS); + SVCSTR(WMI_SERVICE_ARPNS_OFFLOAD); + SVCSTR(WMI_SERVICE_NLO); + SVCSTR(WMI_SERVICE_GTK_OFFLOAD); + SVCSTR(WMI_SERVICE_SCAN_SCH); + SVCSTR(WMI_SERVICE_CSA_OFFLOAD); + SVCSTR(WMI_SERVICE_CHATTER); + SVCSTR(WMI_SERVICE_COEX_FREQAVOID); + SVCSTR(WMI_SERVICE_PACKET_POWER_SAVE); + SVCSTR(WMI_SERVICE_FORCE_FW_HANG); + SVCSTR(WMI_SERVICE_GPIO); + SVCSTR(WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM); + SVCSTR(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG); + SVCSTR(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG); + SVCSTR(WMI_SERVICE_STA_KEEP_ALIVE); + SVCSTR(WMI_SERVICE_TX_ENCAP); + SVCSTR(WMI_SERVICE_BURST); + SVCSTR(WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT); + SVCSTR(WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT); default: - return "UNKNOWN SERVICE\n"; + return NULL; } + +#undef SVCSTR } +#define WMI_MAX_SERVICE 64 + +#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \ + (__le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \ + BIT((svc_id)%(sizeof(u32)))) + +#define SVCMAP(x, y) \ + do { \ + if (WMI_SERVICE_IS_ENABLED((in), (x))) \ + __set_bit(y, out); \ + } while (0) + +static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out) +{ + SVCMAP(WMI_10X_SERVICE_BEACON_OFFLOAD, + WMI_SERVICE_BEACON_OFFLOAD); + SVCMAP(WMI_10X_SERVICE_SCAN_OFFLOAD, + WMI_SERVICE_SCAN_OFFLOAD); + SVCMAP(WMI_10X_SERVICE_ROAM_OFFLOAD, + WMI_SERVICE_ROAM_OFFLOAD); + SVCMAP(WMI_10X_SERVICE_BCN_MISS_OFFLOAD, + WMI_SERVICE_BCN_MISS_OFFLOAD); + SVCMAP(WMI_10X_SERVICE_STA_PWRSAVE, + WMI_SERVICE_STA_PWRSAVE); + SVCMAP(WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_SERVICE_STA_ADVANCED_PWRSAVE); + SVCMAP(WMI_10X_SERVICE_AP_UAPSD, + WMI_SERVICE_AP_UAPSD); + SVCMAP(WMI_10X_SERVICE_AP_DFS, + WMI_SERVICE_AP_DFS); + SVCMAP(WMI_10X_SERVICE_11AC, + WMI_SERVICE_11AC); + SVCMAP(WMI_10X_SERVICE_BLOCKACK, + WMI_SERVICE_BLOCKACK); + SVCMAP(WMI_10X_SERVICE_PHYERR, + WMI_SERVICE_PHYERR); + SVCMAP(WMI_10X_SERVICE_BCN_FILTER, + WMI_SERVICE_BCN_FILTER); + SVCMAP(WMI_10X_SERVICE_RTT, + WMI_SERVICE_RTT); + SVCMAP(WMI_10X_SERVICE_RATECTRL, + WMI_SERVICE_RATECTRL); + SVCMAP(WMI_10X_SERVICE_WOW, + WMI_SERVICE_WOW); + SVCMAP(WMI_10X_SERVICE_RATECTRL_CACHE, + WMI_SERVICE_RATECTRL_CACHE); + SVCMAP(WMI_10X_SERVICE_IRAM_TIDS, + WMI_SERVICE_IRAM_TIDS); + SVCMAP(WMI_10X_SERVICE_BURST, + WMI_SERVICE_BURST); + SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT, + WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT); + SVCMAP(WMI_10X_SERVICE_FORCE_FW_HANG, + WMI_SERVICE_FORCE_FW_HANG); + SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT, + WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT); +} + +static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out) +{ + SVCMAP(WMI_MAIN_SERVICE_BEACON_OFFLOAD, + WMI_SERVICE_BEACON_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_SCAN_OFFLOAD, + WMI_SERVICE_SCAN_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_ROAM_OFFLOAD, + WMI_SERVICE_ROAM_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD, + WMI_SERVICE_BCN_MISS_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_STA_PWRSAVE, + WMI_SERVICE_STA_PWRSAVE); + SVCMAP(WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_SERVICE_STA_ADVANCED_PWRSAVE); + SVCMAP(WMI_MAIN_SERVICE_AP_UAPSD, + WMI_SERVICE_AP_UAPSD); + SVCMAP(WMI_MAIN_SERVICE_AP_DFS, + WMI_SERVICE_AP_DFS); + SVCMAP(WMI_MAIN_SERVICE_11AC, + WMI_SERVICE_11AC); + SVCMAP(WMI_MAIN_SERVICE_BLOCKACK, + WMI_SERVICE_BLOCKACK); + SVCMAP(WMI_MAIN_SERVICE_PHYERR, + WMI_SERVICE_PHYERR); + SVCMAP(WMI_MAIN_SERVICE_BCN_FILTER, + WMI_SERVICE_BCN_FILTER); + SVCMAP(WMI_MAIN_SERVICE_RTT, + WMI_SERVICE_RTT); + SVCMAP(WMI_MAIN_SERVICE_RATECTRL, + WMI_SERVICE_RATECTRL); + SVCMAP(WMI_MAIN_SERVICE_WOW, + WMI_SERVICE_WOW); + SVCMAP(WMI_MAIN_SERVICE_RATECTRL_CACHE, + WMI_SERVICE_RATECTRL_CACHE); + SVCMAP(WMI_MAIN_SERVICE_IRAM_TIDS, + WMI_SERVICE_IRAM_TIDS); + SVCMAP(WMI_MAIN_SERVICE_ARPNS_OFFLOAD, + WMI_SERVICE_ARPNS_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_NLO, + WMI_SERVICE_NLO); + SVCMAP(WMI_MAIN_SERVICE_GTK_OFFLOAD, + WMI_SERVICE_GTK_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_SCAN_SCH, + WMI_SERVICE_SCAN_SCH); + SVCMAP(WMI_MAIN_SERVICE_CSA_OFFLOAD, + WMI_SERVICE_CSA_OFFLOAD); + SVCMAP(WMI_MAIN_SERVICE_CHATTER, + WMI_SERVICE_CHATTER); + SVCMAP(WMI_MAIN_SERVICE_COEX_FREQAVOID, + WMI_SERVICE_COEX_FREQAVOID); + SVCMAP(WMI_MAIN_SERVICE_PACKET_POWER_SAVE, + WMI_SERVICE_PACKET_POWER_SAVE); + SVCMAP(WMI_MAIN_SERVICE_FORCE_FW_HANG, + WMI_SERVICE_FORCE_FW_HANG); + SVCMAP(WMI_MAIN_SERVICE_GPIO, + WMI_SERVICE_GPIO); + SVCMAP(WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM); + SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG); + SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG); + SVCMAP(WMI_MAIN_SERVICE_STA_KEEP_ALIVE, + WMI_SERVICE_STA_KEEP_ALIVE); + SVCMAP(WMI_MAIN_SERVICE_TX_ENCAP, + WMI_SERVICE_TX_ENCAP); +} + +#undef SVCMAP #define WMI_SERVICE_BM_SIZE \ ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32)) @@ -803,6 +966,159 @@ enum wmi_10x_event_id { WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1, }; +enum wmi_10_2_cmd_id { + WMI_10_2_START_CMDID = 0x9000, + WMI_10_2_END_CMDID = 0x9FFF, + WMI_10_2_INIT_CMDID, + WMI_10_2_START_SCAN_CMDID = WMI_10_2_START_CMDID, + WMI_10_2_STOP_SCAN_CMDID, + WMI_10_2_SCAN_CHAN_LIST_CMDID, + WMI_10_2_ECHO_CMDID, + WMI_10_2_PDEV_SET_REGDOMAIN_CMDID, + WMI_10_2_PDEV_SET_CHANNEL_CMDID, + WMI_10_2_PDEV_SET_PARAM_CMDID, + WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID, + WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID, + WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID, + WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID, + WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID, + WMI_10_2_PDEV_SET_QUIET_MODE_CMDID, + WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID, + WMI_10_2_VDEV_CREATE_CMDID, + WMI_10_2_VDEV_DELETE_CMDID, + WMI_10_2_VDEV_START_REQUEST_CMDID, + WMI_10_2_VDEV_RESTART_REQUEST_CMDID, + WMI_10_2_VDEV_UP_CMDID, + WMI_10_2_VDEV_STOP_CMDID, + WMI_10_2_VDEV_DOWN_CMDID, + WMI_10_2_VDEV_STANDBY_RESPONSE_CMDID, + WMI_10_2_VDEV_RESUME_RESPONSE_CMDID, + WMI_10_2_VDEV_SET_PARAM_CMDID, + WMI_10_2_VDEV_INSTALL_KEY_CMDID, + WMI_10_2_VDEV_SET_DSCP_TID_MAP_CMDID, + WMI_10_2_PEER_CREATE_CMDID, + WMI_10_2_PEER_DELETE_CMDID, + WMI_10_2_PEER_FLUSH_TIDS_CMDID, + WMI_10_2_PEER_SET_PARAM_CMDID, + WMI_10_2_PEER_ASSOC_CMDID, + WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID, + WMI_10_2_PEER_UPDATE_WDS_ENTRY_CMDID, + WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_10_2_PEER_MCAST_GROUP_CMDID, + WMI_10_2_BCN_TX_CMDID, + WMI_10_2_BCN_PRB_TMPL_CMDID, + WMI_10_2_BCN_FILTER_RX_CMDID, + WMI_10_2_PRB_REQ_FILTER_RX_CMDID, + WMI_10_2_MGMT_TX_CMDID, + WMI_10_2_ADDBA_CLEAR_RESP_CMDID, + WMI_10_2_ADDBA_SEND_CMDID, + WMI_10_2_ADDBA_STATUS_CMDID, + WMI_10_2_DELBA_SEND_CMDID, + WMI_10_2_ADDBA_SET_RESP_CMDID, + WMI_10_2_SEND_SINGLEAMSDU_CMDID, + WMI_10_2_STA_POWERSAVE_MODE_CMDID, + WMI_10_2_STA_POWERSAVE_PARAM_CMDID, + WMI_10_2_STA_MIMO_PS_MODE_CMDID, + WMI_10_2_DBGLOG_CFG_CMDID, + WMI_10_2_PDEV_DFS_ENABLE_CMDID, + WMI_10_2_PDEV_DFS_DISABLE_CMDID, + WMI_10_2_PDEV_QVIT_CMDID, + WMI_10_2_ROAM_SCAN_MODE, + WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD, + WMI_10_2_ROAM_SCAN_PERIOD, + WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_10_2_ROAM_AP_PROFILE, + WMI_10_2_OFL_SCAN_ADD_AP_PROFILE, + WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_10_2_OFL_SCAN_PERIOD, + WMI_10_2_P2P_DEV_SET_DEVICE_INFO, + WMI_10_2_P2P_DEV_SET_DISCOVERABILITY, + WMI_10_2_P2P_GO_SET_BEACON_IE, + WMI_10_2_P2P_GO_SET_PROBE_RESP_IE, + WMI_10_2_AP_PS_PEER_PARAM_CMDID, + WMI_10_2_AP_PS_PEER_UAPSD_COEX_CMDID, + WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID, + WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID, + WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + WMI_10_2_PDEV_SUSPEND_CMDID, + WMI_10_2_PDEV_RESUME_CMDID, + WMI_10_2_ADD_BCN_FILTER_CMDID, + WMI_10_2_RMV_BCN_FILTER_CMDID, + WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID, + WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_10_2_WOW_ENABLE_CMDID, + WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + WMI_10_2_RTT_MEASREQ_CMDID, + WMI_10_2_RTT_TSF_CMDID, + WMI_10_2_RTT_KEEPALIVE_CMDID, + WMI_10_2_PDEV_SEND_BCN_CMDID, + WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_10_2_REQUEST_STATS_CMDID, + WMI_10_2_GPIO_CONFIG_CMDID, + WMI_10_2_GPIO_OUTPUT_CMDID, + WMI_10_2_VDEV_RATEMASK_CMDID, + WMI_10_2_PDEV_SMART_ANT_ENABLE_CMDID, + WMI_10_2_PDEV_SMART_ANT_SET_RX_ANTENNA_CMDID, + WMI_10_2_PEER_SMART_ANT_SET_TX_ANTENNA_CMDID, + WMI_10_2_PEER_SMART_ANT_SET_TRAIN_INFO_CMDID, + WMI_10_2_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMDID, + WMI_10_2_FORCE_FW_HANG_CMDID, + WMI_10_2_PDEV_SET_ANTENNA_SWITCH_TABLE_CMDID, + WMI_10_2_PDEV_SET_CTL_TABLE_CMDID, + WMI_10_2_PDEV_SET_MIMOGAIN_TABLE_CMDID, + WMI_10_2_PDEV_RATEPWR_TABLE_CMDID, + WMI_10_2_PDEV_RATEPWR_CHAINMSK_TABLE_CMDID, + WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1, +}; + +enum wmi_10_2_event_id { + WMI_10_2_SERVICE_READY_EVENTID = 0x8000, + WMI_10_2_READY_EVENTID, + WMI_10_2_DEBUG_MESG_EVENTID, + WMI_10_2_START_EVENTID = 0x9000, + WMI_10_2_END_EVENTID = 0x9FFF, + WMI_10_2_SCAN_EVENTID = WMI_10_2_START_EVENTID, + WMI_10_2_ECHO_EVENTID, + WMI_10_2_UPDATE_STATS_EVENTID, + WMI_10_2_INST_RSSI_STATS_EVENTID, + WMI_10_2_VDEV_START_RESP_EVENTID, + WMI_10_2_VDEV_STANDBY_REQ_EVENTID, + WMI_10_2_VDEV_RESUME_REQ_EVENTID, + WMI_10_2_VDEV_STOPPED_EVENTID, + WMI_10_2_PEER_STA_KICKOUT_EVENTID, + WMI_10_2_HOST_SWBA_EVENTID, + WMI_10_2_TBTTOFFSET_UPDATE_EVENTID, + WMI_10_2_MGMT_RX_EVENTID, + WMI_10_2_CHAN_INFO_EVENTID, + WMI_10_2_PHYERR_EVENTID, + WMI_10_2_ROAM_EVENTID, + WMI_10_2_PROFILE_MATCH, + WMI_10_2_DEBUG_PRINT_EVENTID, + WMI_10_2_PDEV_QVIT_EVENTID, + WMI_10_2_WLAN_PROFILE_DATA_EVENTID, + WMI_10_2_RTT_MEASUREMENT_REPORT_EVENTID, + WMI_10_2_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_10_2_RTT_ERROR_REPORT_EVENTID, + WMI_10_2_RTT_KEEPALIVE_EVENTID, + WMI_10_2_WOW_WAKEUP_HOST_EVENTID, + WMI_10_2_DCS_INTERFERENCE_EVENTID, + WMI_10_2_PDEV_TPC_CONFIG_EVENTID, + WMI_10_2_GPIO_INPUT_EVENTID, + WMI_10_2_PEER_RATECODE_LIST_EVENTID, + WMI_10_2_GENERIC_BUFFER_EVENTID, + WMI_10_2_MCAST_BUF_RELEASE_EVENTID, + WMI_10_2_MCAST_LIST_AGEOUT_EVENTID, + WMI_10_2_WDS_PEER_EVENTID, + WMI_10_2_PDEV_UTF_EVENTID = WMI_10_2_END_EVENTID - 1, +}; + enum wmi_phy_mode { MODE_11A = 0, /* 11a Mode */ MODE_11G = 1, /* 11b/g Mode */ @@ -1076,10 +1392,6 @@ struct wlan_host_mem_req { __le32 num_units; } __packed; -#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \ - ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \ - (1 << ((svc_id)%(sizeof(u32))))) != 0) - /* * The following struct holds optional payload for * wmi_service_ready_event,e.g., 11ac pass some of the @@ -1551,6 +1863,16 @@ struct wmi_resource_config_10x { __le32 max_frag_entries; } __packed; +struct wmi_resource_config_10_2 { + struct wmi_resource_config_10x common; + __le32 max_peer_ext_stats; + __le32 smart_ant_cap; /* 0-disable, 1-enable */ + __le32 bk_min_free; + __le32 be_min_free; + __le32 vi_min_free; + __le32 vo_min_free; + __le32 rx_batchmode; /* 0-disable, 1-enable */ +} __packed; #define NUM_UNITS_IS_NUM_VDEVS 0x1 #define NUM_UNITS_IS_NUM_PEERS 0x2 @@ -1588,11 +1910,28 @@ struct wmi_init_cmd_10x { struct host_memory_chunk host_mem_chunks[1]; } __packed; +struct wmi_init_cmd_10_2 { + struct wmi_resource_config_10_2 resource_config; + __le32 num_host_mem_chunks; + + /* + * variable number of host memory chunks. + * This should be the last element in the structure + */ + struct host_memory_chunk host_mem_chunks[1]; +} __packed; + +struct wmi_chan_list_entry { + __le16 freq; + u8 phy_mode; /* valid for 10.2 only */ + u8 reserved; +} __packed; + /* TLV for channel list */ struct wmi_chan_list { __le32 tag; /* WMI_CHAN_LIST_TAG */ __le32 num_chan; - __le32 channel_list[0]; + struct wmi_chan_list_entry channel_list[0]; } __packed; struct wmi_bssid_list { @@ -1821,7 +2160,7 @@ struct wmi_start_scan_arg { u32 n_bssids; u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; - u32 channels[64]; + u16 channels[64]; struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; }; @@ -2067,6 +2406,7 @@ struct wmi_comb_phyerr_rx_event { #define PHYERR_TLV_SIG 0xBB #define PHYERR_TLV_TAG_SEARCH_FFT_REPORT 0xFB #define PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY 0xF8 +#define PHYERR_TLV_TAG_SPECTRAL_SUMMARY_REPORT 0xF9 struct phyerr_radar_report { __le32 reg0; /* RADAR_REPORT_REG0_* */ @@ -2515,6 +2855,19 @@ enum wmi_10x_pdev_param { WMI_10X_PDEV_PARAM_BURST_DUR, /* Set Bursting Enable*/ WMI_10X_PDEV_PARAM_BURST_ENABLE, + + /* following are available as of firmware 10.2 */ + WMI_10X_PDEV_PARAM_SMART_ANTENNA_DEFAULT_ANTENNA, + WMI_10X_PDEV_PARAM_IGMPMLD_OVERRIDE, + WMI_10X_PDEV_PARAM_IGMPMLD_TID, + WMI_10X_PDEV_PARAM_ANTENNA_GAIN, + WMI_10X_PDEV_PARAM_RX_DECAP_MODE, + WMI_10X_PDEV_PARAM_RX_FILTER, + WMI_10X_PDEV_PARAM_SET_MCAST_TO_UCAST_TID, + WMI_10X_PDEV_PARAM_PROXY_STA_MODE, + WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_MODE, + WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_BUFFER, + WMI_10X_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER, }; struct wmi_pdev_set_param_cmd { @@ -3387,6 +3740,14 @@ enum wmi_10x_vdev_param { WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, + + /* following are available as of firmware 10.2 */ + WMI_10X_VDEV_PARAM_TX_ENCAP_TYPE, + WMI_10X_VDEV_PARAM_CABQ_MAXDUR, + WMI_10X_VDEV_PARAM_MFPTEST_SET, + WMI_10X_VDEV_PARAM_RTS_FIXED_RATE, + WMI_10X_VDEV_PARAM_VHT_SGIMASK, + WMI_10X_VDEV_PARAM_VHT80_RATEMASK, }; /* slot time long */ @@ -3444,6 +3805,98 @@ struct wmi_vdev_simple_event { /* unsupported VDEV combination */ #define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED 0x2 +/* TODO: please add more comments if you have in-depth information */ +struct wmi_vdev_spectral_conf_cmd { + __le32 vdev_id; + + /* number of fft samples to send (0 for infinite) */ + __le32 scan_count; + __le32 scan_period; + __le32 scan_priority; + + /* number of bins in the FFT: 2^(fft_size - bin_scale) */ + __le32 scan_fft_size; + __le32 scan_gc_ena; + __le32 scan_restart_ena; + __le32 scan_noise_floor_ref; + __le32 scan_init_delay; + __le32 scan_nb_tone_thr; + __le32 scan_str_bin_thr; + __le32 scan_wb_rpt_mode; + __le32 scan_rssi_rpt_mode; + __le32 scan_rssi_thr; + __le32 scan_pwr_format; + + /* rpt_mode: Format of FFT report to software for spectral scan + * triggered FFTs: + * 0: No FFT report (only spectral scan summary report) + * 1: 2-dword summary of metrics for each completed FFT + spectral + * scan summary report + * 2: 2-dword summary of metrics for each completed FFT + + * 1x- oversampled bins(in-band) per FFT + spectral scan summary + * report + * 3: 2-dword summary of metrics for each completed FFT + + * 2x- oversampled bins (all) per FFT + spectral scan summary + */ + __le32 scan_rpt_mode; + __le32 scan_bin_scale; + __le32 scan_dbm_adj; + __le32 scan_chn_mask; +} __packed; + +struct wmi_vdev_spectral_conf_arg { + u32 vdev_id; + u32 scan_count; + u32 scan_period; + u32 scan_priority; + u32 scan_fft_size; + u32 scan_gc_ena; + u32 scan_restart_ena; + u32 scan_noise_floor_ref; + u32 scan_init_delay; + u32 scan_nb_tone_thr; + u32 scan_str_bin_thr; + u32 scan_wb_rpt_mode; + u32 scan_rssi_rpt_mode; + u32 scan_rssi_thr; + u32 scan_pwr_format; + u32 scan_rpt_mode; + u32 scan_bin_scale; + u32 scan_dbm_adj; + u32 scan_chn_mask; +}; + +#define WMI_SPECTRAL_ENABLE_DEFAULT 0 +#define WMI_SPECTRAL_COUNT_DEFAULT 0 +#define WMI_SPECTRAL_PERIOD_DEFAULT 35 +#define WMI_SPECTRAL_PRIORITY_DEFAULT 1 +#define WMI_SPECTRAL_FFT_SIZE_DEFAULT 7 +#define WMI_SPECTRAL_GC_ENA_DEFAULT 1 +#define WMI_SPECTRAL_RESTART_ENA_DEFAULT 0 +#define WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT -96 +#define WMI_SPECTRAL_INIT_DELAY_DEFAULT 80 +#define WMI_SPECTRAL_NB_TONE_THR_DEFAULT 12 +#define WMI_SPECTRAL_STR_BIN_THR_DEFAULT 8 +#define WMI_SPECTRAL_WB_RPT_MODE_DEFAULT 0 +#define WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT 0 +#define WMI_SPECTRAL_RSSI_THR_DEFAULT 0xf0 +#define WMI_SPECTRAL_PWR_FORMAT_DEFAULT 0 +#define WMI_SPECTRAL_RPT_MODE_DEFAULT 2 +#define WMI_SPECTRAL_BIN_SCALE_DEFAULT 1 +#define WMI_SPECTRAL_DBM_ADJ_DEFAULT 1 +#define WMI_SPECTRAL_CHN_MASK_DEFAULT 1 + +struct wmi_vdev_spectral_enable_cmd { + __le32 vdev_id; + __le32 trigger_cmd; + __le32 enable_cmd; +} __packed; + +#define WMI_SPECTRAL_TRIGGER_CMD_TRIGGER 1 +#define WMI_SPECTRAL_TRIGGER_CMD_CLEAR 2 +#define WMI_SPECTRAL_ENABLE_CMD_ENABLE 1 +#define WMI_SPECTRAL_ENABLE_CMD_DISABLE 2 + /* Beacon processing related command and event structures */ struct wmi_bcn_tx_hdr { __le32 vdev_id; @@ -3470,6 +3923,11 @@ enum wmi_bcn_tx_ref_flags { WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2, }; +/* TODO: It is unclear why "no antenna" works while any other seemingly valid + * chainmask yields no beacons on the air at all. + */ +#define WMI_BCN_TX_REF_DEF_ANTENNA 0 + struct wmi_bcn_tx_ref_cmd { __le32 vdev_id; __le32 data_len; @@ -3481,6 +3939,8 @@ struct wmi_bcn_tx_ref_cmd { __le32 frame_control; /* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */ __le32 flags; + /* introduced in 10.2 */ + __le32 antenna_mask; } __packed; /* Beacon filter */ @@ -4053,7 +4513,7 @@ struct wmi_peer_set_q_empty_callback_cmd { /* Maximum listen interval supported by hw in units of beacon interval */ #define ATH10K_MAX_HW_LISTEN_INTERVAL 5 -struct wmi_peer_assoc_complete_cmd { +struct wmi_common_peer_assoc_complete_cmd { struct wmi_mac_addr peer_macaddr; __le32 vdev_id; __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */ @@ -4071,11 +4531,30 @@ struct wmi_peer_assoc_complete_cmd { __le32 peer_vht_caps; __le32 peer_phymode; struct wmi_vht_rate_set peer_vht_rates; +}; + +struct wmi_main_peer_assoc_complete_cmd { + struct wmi_common_peer_assoc_complete_cmd cmd; + /* HT Operation Element of the peer. Five bytes packed in 2 * INT32 array and filled from lsb to msb. */ __le32 peer_ht_info[2]; } __packed; +struct wmi_10_1_peer_assoc_complete_cmd { + struct wmi_common_peer_assoc_complete_cmd cmd; +} __packed; + +#define WMI_PEER_ASSOC_INFO0_MAX_MCS_IDX_LSB 0 +#define WMI_PEER_ASSOC_INFO0_MAX_MCS_IDX_MASK 0x0f +#define WMI_PEER_ASSOC_INFO0_MAX_NSS_LSB 4 +#define WMI_PEER_ASSOC_INFO0_MAX_NSS_MASK 0xf0 + +struct wmi_10_2_peer_assoc_complete_cmd { + struct wmi_common_peer_assoc_complete_cmd cmd; + __le32 info0; /* WMI_PEER_ASSOC_INFO0_ */ +} __packed; + struct wmi_peer_assoc_complete_arg { u8 addr[ETH_ALEN]; u32 vdev_id; @@ -4290,6 +4769,10 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, u32 param_id, u32 param_value); int ath10k_wmi_vdev_install_key(struct ath10k *ar, const struct wmi_vdev_install_key_arg *arg); +int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg); +int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, + u32 enable); int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, const u8 peer_addr[ETH_ALEN]); int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index 7106547a14dd..66b6366158b9 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -351,8 +351,7 @@ void ath5k_hw_deinit(struct ath5k_hw *ah) { __set_bit(ATH_STAT_INVALID, ah->status); - if (ah->ah_rf_banks != NULL) - kfree(ah->ah_rf_banks); + kfree(ah->ah_rf_banks); ath5k_eeprom_detach(ah); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 8ad2550bce7f..59a87247aac4 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1423,7 +1423,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, break; } - if (rxs->rate_idx >= 0 && rs->rs_rate == + if (rs->rs_rate == ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short) rxs->flag |= RX_FLAG_SHORTPRE; diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index b8d031ae63c2..30e4e1fd4b04 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -894,6 +894,100 @@ static const struct file_operations fops_queue = { .llseek = default_llseek, }; +/* debugfs: eeprom */ + +struct eeprom_private { + u16 *buf; + int len; +}; + +static int open_file_eeprom(struct inode *inode, struct file *file) +{ + struct eeprom_private *ep; + struct ath5k_hw *ah = inode->i_private; + bool res; + int i, ret; + u32 eesize; + u16 val, *buf; + + /* Get eeprom size */ + + res = ath5k_hw_nvram_read(ah, AR5K_EEPROM_SIZE_UPPER, &val); + if (!res) + return -EACCES; + + if (val == 0) { + eesize = AR5K_EEPROM_INFO_MAX + AR5K_EEPROM_INFO_BASE; + } else { + eesize = (val & AR5K_EEPROM_SIZE_UPPER_MASK) << + AR5K_EEPROM_SIZE_ENDLOC_SHIFT; + ath5k_hw_nvram_read(ah, AR5K_EEPROM_SIZE_LOWER, &val); + eesize = eesize | val; + } + + if (eesize > 4096) + return -EINVAL; + + /* Create buffer and read in eeprom */ + + buf = vmalloc(eesize); + if (!buf) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < eesize; ++i) { + AR5K_EEPROM_READ(i, val); + buf[i] = val; + } + + /* Create private struct and assign to file */ + + ep = kmalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) { + ret = -ENOMEM; + goto freebuf; + } + + ep->buf = buf; + ep->len = i; + + file->private_data = (void *)ep; + + return 0; + +freebuf: + vfree(buf); +err: + return ret; + +} + +static ssize_t read_file_eeprom(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct eeprom_private *ep = file->private_data; + + return simple_read_from_buffer(user_buf, count, ppos, ep->buf, ep->len); +} + +static int release_file_eeprom(struct inode *inode, struct file *file) +{ + struct eeprom_private *ep = file->private_data; + + vfree(ep->buf); + kfree(ep); + + return 0; +} + +static const struct file_operations fops_eeprom = { + .open = open_file_eeprom, + .read = read_file_eeprom, + .release = release_file_eeprom, + .owner = THIS_MODULE, +}; + void ath5k_debug_init_device(struct ath5k_hw *ah) @@ -921,6 +1015,8 @@ ath5k_debug_init_device(struct ath5k_hw *ah) debugfs_create_file("misc", S_IRUSR, phydir, ah, &fops_misc); + debugfs_create_file("eeprom", S_IRUSR, phydir, ah, &fops_eeprom); + debugfs_create_file("frameerrors", S_IWUSR | S_IRUSR, phydir, ah, &fops_frameerrors); diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index 48a6a69b57bc..2062d1190556 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -130,6 +130,7 @@ ath5k_register_led(struct ath5k_hw *ah, struct ath5k_led *led, led->ah = ah; strncpy(led->name, name, sizeof(led->name)); + led->name[sizeof(led->name)-1] = 0; led->led_dev.name = led->name; led->led_dev.default_trigger = trigger; led->led_dev.brightness_set = ath5k_led_brightness_set; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e535807c3d89..ba60e37213eb 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -717,6 +717,7 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, memcpy(ie + 2, vif->ssid, vif->ssid_len); memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len); bss = cfg80211_inform_bss(ar->wiphy, chan, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, cap_val, 100, ie, 2 + vif->ssid_len + beacon_ie_len, 0, GFP_KERNEL); diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index fffd52355123..6e473fa4b13c 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1049,7 +1049,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->hw.reserved_ram_size = le32_to_cpup(val); ath6kl_dbg(ATH6KL_DBG_BOOT, - "found reserved ram size ie 0x%d\n", + "found reserved ram size ie %d\n", ar->hw.reserved_ram_size); break; case ATH6KL_FW_IE_CAPABILITIES: diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 21516bc65785..933aef025698 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -225,7 +225,7 @@ int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value) ret = ath6kl_hif_diag_write32(ar, address, value); if (ret) { - ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n", + ath6kl_err("failed to write 0x%x during diagnose window to 0x%x\n", address, value); return ret; } diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 339d89f14d32..eab0ab976af2 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -1400,6 +1400,7 @@ static const struct sdio_device_id ath6kl_sdio_devices[] = { {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))}, {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))}, + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))}, {}, }; diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index c44325856b81..a6a5e40b3e98 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1229,26 +1229,7 @@ static struct usb_driver ath6kl_usb_driver = { .disable_hub_initiated_lpm = 1, }; -static int ath6kl_usb_init(void) -{ - int ret; - - ret = usb_register(&ath6kl_usb_driver); - if (ret) { - ath6kl_err("usb registration failed: %d\n", ret); - return ret; - } - - return 0; -} - -static void ath6kl_usb_exit(void) -{ - usb_deregister(&ath6kl_usb_driver); -} - -module_init(ath6kl_usb_init); -module_exit(ath6kl_usb_exit); +module_usb_driver(ath6kl_usb_driver); MODULE_AUTHOR("Atheros Communications, Inc."); MODULE_DESCRIPTION("Driver support for Atheros AR600x USB devices"); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 94df345d08c2..b921005ad7ee 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -619,8 +619,7 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, - GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0); return 0; } @@ -659,7 +658,7 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0); return 0; } @@ -1093,7 +1092,6 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, u8 *buf; struct ieee80211_channel *channel; struct ath6kl *ar = wmi->parent_dev; - struct ieee80211_mgmt *mgmt; struct cfg80211_bss *bss; if (len <= sizeof(struct wmi_bss_info_hdr2)) @@ -1139,39 +1137,15 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, } } - /* - * In theory, use of cfg80211_inform_bss() would be more natural here - * since we do not have the full frame. However, at least for now, - * cfg80211 can only distinguish Beacon and Probe Response frames from - * each other when using cfg80211_inform_bss_frame(), so let's build a - * fake IEEE 802.11 header to be able to take benefit of this. - */ - mgmt = kmalloc(24 + len, GFP_ATOMIC); - if (mgmt == NULL) - return -EINVAL; - - if (bih->frame_type == BEACON_FTYPE) { - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_BEACON); - memset(mgmt->da, 0xff, ETH_ALEN); - } else { - struct net_device *dev = vif->ndev; - - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_RESP); - memcpy(mgmt->da, dev->dev_addr, ETH_ALEN); - } - mgmt->duration = cpu_to_le16(0); - memcpy(mgmt->sa, bih->bssid, ETH_ALEN); - memcpy(mgmt->bssid, bih->bssid, ETH_ALEN); - mgmt->seq_ctrl = cpu_to_le16(0); - - memcpy(&mgmt->u.beacon, buf, len); - - bss = cfg80211_inform_bss_frame(ar->wiphy, channel, mgmt, - 24 + len, (bih->snr - 95) * 100, - GFP_ATOMIC); - kfree(mgmt); + bss = cfg80211_inform_bss(ar->wiphy, channel, + bih->frame_type == BEACON_FTYPE ? + CFG80211_BSS_FTYPE_BEACON : + CFG80211_BSS_FTYPE_PRESP, + bih->bssid, get_unaligned_le64((__le64 *)buf), + get_unaligned_le16(((__le16 *)buf) + 5), + get_unaligned_le16(((__le16 *)buf) + 4), + buf + 8 + 2 + 2, len - 8 - 2 - 2, + (bih->snr - 95) * 100, GFP_ATOMIC); if (bss == NULL) return -ENOMEM; cfg80211_put_bss(ar->wiphy, bss); diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 8fcc029a76a6..b8f570ed39ad 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -130,6 +130,15 @@ config ATH9K_RFKILL seconds. Turn off to save power, but enable it if you have a platform that can toggle the RF-Kill GPIO. +config ATH9K_CHANNEL_CONTEXT + bool "Channel Context support" + depends on ATH9K + default n + ---help--- + This option enables channel context support in ath9k, which is needed + for multi-channel concurrency. Enable this if P2P PowerSave support + is required. + config ATH9K_HTC tristate "Atheros HTC based wireless cards support" depends on USB && MAC80211 diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7fc13a8da675..c690601f7276 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -31,6 +31,7 @@ #include "spectral.h" struct ath_node; +struct ath_vif; extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; @@ -324,6 +325,10 @@ struct ath_rx { u32 ampdu_ref; }; +/*******************/ +/* Channel Context */ +/*******************/ + struct ath_chanctx { struct cfg80211_chan_def chandef; struct list_head vifs; @@ -354,7 +359,9 @@ enum ath_chanctx_event { ATH_CHANCTX_EVENT_BEACON_RECEIVED, ATH_CHANCTX_EVENT_ASSOC, ATH_CHANCTX_EVENT_SWITCH, + ATH_CHANCTX_EVENT_ASSIGN, ATH_CHANCTX_EVENT_UNASSIGN, + ATH_CHANCTX_EVENT_CHANGE, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL, }; @@ -403,35 +410,121 @@ struct ath_offchannel { int roc_duration; int duration; }; + +#define case_rtn_string(val) case val: return #val + #define ath_for_each_chanctx(_sc, _ctx) \ for (ctx = &sc->chanctx[0]; \ ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1]; \ ctx++) -void ath9k_fill_chanctx_ops(void); -void ath9k_chanctx_force_active(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); +void ath_chanctx_init(struct ath_softc *sc); +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef); + +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + static inline struct ath_chanctx * ath_chanctx_get(struct ieee80211_chanctx_conf *ctx) { struct ath_chanctx **ptr = (void *) ctx->drv_priv; return *ptr; } -void ath_chanctx_init(struct ath_softc *sc); -void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, - struct cfg80211_chan_def *chandef); -void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, - struct cfg80211_chan_def *chandef); + +bool ath9k_is_chanctx_enabled(void); +void ath9k_fill_chanctx_ops(void); +void ath9k_init_channel_context(struct ath_softc *sc); +void ath9k_offchannel_init(struct ath_softc *sc); +void ath9k_deinit_channel_context(struct ath_softc *sc); +int ath9k_init_p2p(struct ath_softc *sc); +void ath9k_deinit_p2p(struct ath_softc *sc); +void ath9k_p2p_remove_vif(struct ath_softc *sc, + struct ieee80211_vif *vif); +void ath9k_p2p_beacon_sync(struct ath_softc *sc); +void ath9k_p2p_bss_info_changed(struct ath_softc *sc, + struct ieee80211_vif *vif); +void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb); +void ath9k_p2p_ps_timer(void *priv); +void ath9k_chanctx_wake_queues(struct ath_softc *sc); void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx); -void ath_offchannel_timer(unsigned long data); -void ath_offchannel_channel_change(struct ath_softc *sc); -void ath_chanctx_offchan_switch(struct ath_softc *sc, - struct ieee80211_channel *chan); -struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, - bool active); + +void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts, + enum ath_chanctx_event ev); +void ath_chanctx_beacon_sent_ev(struct ath_softc *sc, + enum ath_chanctx_event ev); void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, enum ath_chanctx_event ev); -void ath_chanctx_timer(unsigned long data); +void ath_chanctx_set_next(struct ath_softc *sc, bool force); +void ath_offchannel_next(struct ath_softc *sc); +void ath_scan_complete(struct ath_softc *sc, bool abort); +void ath_roc_complete(struct ath_softc *sc, bool abort); + +#else + +static inline bool ath9k_is_chanctx_enabled(void) +{ + return false; +} +static inline void ath9k_fill_chanctx_ops(void) +{ +} +static inline void ath9k_init_channel_context(struct ath_softc *sc) +{ +} +static inline void ath9k_offchannel_init(struct ath_softc *sc) +{ +} +static inline void ath9k_deinit_channel_context(struct ath_softc *sc) +{ +} +static inline void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts, + enum ath_chanctx_event ev) +{ +} +static inline void ath_chanctx_beacon_sent_ev(struct ath_softc *sc, + enum ath_chanctx_event ev) +{ +} +static inline void ath_chanctx_event(struct ath_softc *sc, + struct ieee80211_vif *vif, + enum ath_chanctx_event ev) +{ +} +static inline int ath9k_init_p2p(struct ath_softc *sc) +{ + return 0; +} +static inline void ath9k_deinit_p2p(struct ath_softc *sc) +{ +} +static inline void ath9k_p2p_remove_vif(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ +} +static inline void ath9k_p2p_beacon_sync(struct ath_softc *sc) +{ +} +static inline void ath9k_p2p_bss_info_changed(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ +} +static inline void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb) +{ +} +static inline void ath9k_p2p_ps_timer(struct ath_softc *sc) +{ +} +static inline void ath9k_chanctx_wake_queues(struct ath_softc *sc) +{ +} +static inline void ath_chanctx_check_active(struct ath_softc *sc, + struct ath_chanctx *ctx) +{ +} + +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); @@ -583,7 +676,6 @@ void ath9k_csa_update(struct ath_softc *sc); #define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PLL_WORK_INTERVAL 100 -void ath_chanctx_work(struct work_struct *work); void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); bool ath_hw_check(struct ath_softc *sc); @@ -597,8 +689,6 @@ int ath_update_survey_stats(struct ath_softc *sc); void ath_update_survey_nf(struct ath_softc *sc, int channel); void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); void ath_ps_full_sleep(unsigned long data); -void ath9k_p2p_ps_timer(void *priv); -void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif); void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop); /**********/ @@ -849,12 +939,17 @@ struct ath_softc { struct mutex mutex; struct work_struct paprd_work; struct work_struct hw_reset_work; - struct work_struct chanctx_work; struct completion paprd_complete; wait_queue_head_t tx_wait; +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + struct work_struct chanctx_work; struct ath_gen_timer *p2p_ps_timer; struct ath_vif *p2p_ps_vif; + struct ath_chanctx_sched sched; + struct ath_offchannel offchannel; + struct ath_chanctx *next_chan; +#endif unsigned long driver_data; @@ -875,10 +970,7 @@ struct ath_softc { struct cfg80211_chan_def cur_chandef; struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX]; struct ath_chanctx *cur_chan; - struct ath_chanctx *next_chan; spinlock_t chan_lock; - struct ath_offchannel offchannel; - struct ath_chanctx_sched sched; #ifdef CONFIG_MAC80211_LEDS bool led_registered; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index eaf8f058c151..b2f56d8b9043 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -108,55 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); } -static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, - struct sk_buff *skb) -{ - static const u8 noa_ie_hdr[] = { - WLAN_EID_VENDOR_SPECIFIC, /* type */ - 0, /* length */ - 0x50, 0x6f, 0x9a, /* WFA OUI */ - 0x09, /* P2P subtype */ - 0x0c, /* Notice of Absence */ - 0x00, /* LSB of little-endian len */ - 0x00, /* MSB of little-endian len */ - }; - - struct ieee80211_p2p_noa_attr *noa; - int noa_len, noa_desc, i = 0; - u8 *hdr; - - if (!avp->offchannel_duration && !avp->periodic_noa_duration) - return; - - noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration; - noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc; - - hdr = skb_put(skb, sizeof(noa_ie_hdr)); - memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr)); - hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2; - hdr[7] = noa_len; - - noa = (void *) skb_put(skb, noa_len); - memset(noa, 0, noa_len); - - noa->index = avp->noa_index; - if (avp->periodic_noa_duration) { - u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); - - noa->desc[i].count = 255; - noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start); - noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration); - noa->desc[i].interval = cpu_to_le32(interval); - i++; - } - - if (avp->offchannel_duration) { - noa->desc[i].count = 1; - noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start); - noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration); - } -} - static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -427,9 +378,10 @@ void ath9k_beacon_tasklet(unsigned long data) /* EDMA devices check that in the tx completion function. */ if (!edma) { - if (sc->sched.beacon_pending) - ath_chanctx_event(sc, NULL, + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_beacon_sent_ev(sc, ATH_CHANCTX_EVENT_BEACON_SENT); + } if (ath9k_csa_is_finished(sc, vif)) return; @@ -438,7 +390,10 @@ void ath9k_beacon_tasklet(unsigned long data) if (!vif || !vif->bss_conf.enable_beacon) return; - ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); + } + bf = ath9k_beacon_generate(sc->hw, vif); if (sc->beacon.bmisscnt != 0) { diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index ba214ebdcd16..409f912a67c7 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -101,6 +101,746 @@ static int ath_set_channel(struct ath_softc *sc) return 0; } +void ath_chanctx_init(struct ath_softc *sc) +{ + struct ath_chanctx *ctx; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + int i, j; + + sband = &common->sbands[IEEE80211_BAND_2GHZ]; + if (!sband->n_channels) + sband = &common->sbands[IEEE80211_BAND_5GHZ]; + + chan = &sband->channels[0]; + for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { + ctx = &sc->chanctx[i]; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + INIT_LIST_HEAD(&ctx->vifs); + ctx->txpower = ATH_TXPOWER_MAX; + for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) + INIT_LIST_HEAD(&ctx->acq[j]); + } +} + +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + bool cur_chan; + + spin_lock_bh(&sc->chan_lock); + if (chandef) + memcpy(&ctx->chandef, chandef, sizeof(*chandef)); + cur_chan = sc->cur_chan == ctx; + spin_unlock_bh(&sc->chan_lock); + + if (!cur_chan) { + ath_dbg(common, CHAN_CTX, + "Current context differs from the new context\n"); + return; + } + + ath_set_channel(sc); +} + +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + +/**********************************************************/ +/* Functions to handle the channel context state machine. */ +/**********************************************************/ + +static const char *offchannel_state_string(enum ath_offchannel_state state) +{ + switch (state) { + case_rtn_string(ATH_OFFCHANNEL_IDLE); + case_rtn_string(ATH_OFFCHANNEL_PROBE_SEND); + case_rtn_string(ATH_OFFCHANNEL_PROBE_WAIT); + case_rtn_string(ATH_OFFCHANNEL_SUSPEND); + case_rtn_string(ATH_OFFCHANNEL_ROC_START); + case_rtn_string(ATH_OFFCHANNEL_ROC_WAIT); + case_rtn_string(ATH_OFFCHANNEL_ROC_DONE); + default: + return "unknown"; + } +} + +static const char *chanctx_event_string(enum ath_chanctx_event ev) +{ + switch (ev) { + case_rtn_string(ATH_CHANCTX_EVENT_BEACON_PREPARE); + case_rtn_string(ATH_CHANCTX_EVENT_BEACON_SENT); + case_rtn_string(ATH_CHANCTX_EVENT_TSF_TIMER); + case_rtn_string(ATH_CHANCTX_EVENT_BEACON_RECEIVED); + case_rtn_string(ATH_CHANCTX_EVENT_ASSOC); + case_rtn_string(ATH_CHANCTX_EVENT_SWITCH); + case_rtn_string(ATH_CHANCTX_EVENT_ASSIGN); + case_rtn_string(ATH_CHANCTX_EVENT_UNASSIGN); + case_rtn_string(ATH_CHANCTX_EVENT_CHANGE); + case_rtn_string(ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); + default: + return "unknown"; + } +} + +static const char *chanctx_state_string(enum ath_chanctx_state state) +{ + switch (state) { + case_rtn_string(ATH_CHANCTX_STATE_IDLE); + case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_BEACON); + case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_TIMER); + case_rtn_string(ATH_CHANCTX_STATE_SWITCH); + case_rtn_string(ATH_CHANCTX_STATE_FORCE_ACTIVE); + default: + return "unknown"; + } +} + +void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp; + bool active = false; + u8 n_active = 0; + + if (!ctx) + return; + + list_for_each_entry(avp, &ctx->vifs, list) { + struct ieee80211_vif *vif = avp->vif; + + switch (vif->type) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + if (vif->bss_conf.assoc) + active = true; + break; + default: + active = true; + break; + } + } + ctx->active = active; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + n_active++; + } + + if (n_active <= 1) { + clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); + return; + } + if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + return; + + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); + } +} + +static struct ath_chanctx * +ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + int idx = ctx - &sc->chanctx[0]; + + return &sc->chanctx[!idx]; +} + +static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) +{ + struct ath_chanctx *prev, *cur; + struct timespec ts; + u32 cur_tsf, prev_tsf, beacon_int; + s32 offset; + + beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + + cur = sc->cur_chan; + prev = ath_chanctx_get_next(sc, cur); + + getrawmonotonic(&ts); + cur_tsf = (u32) cur->tsf_val + + ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); + + prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf; + prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts); + + /* Adjust the TSF time of the AP chanctx to keep its beacons + * at half beacon interval offset relative to the STA chanctx. + */ + offset = cur_tsf - prev_tsf; + + /* Ignore stale data or spurious timestamps */ + if (offset < 0 || offset > 3 * beacon_int) + return; + + offset = beacon_int / 2 - (offset % beacon_int); + prev->tsf_val += offset; +} + +/* Configure the TSF based hardware timer for a channel switch. + * Also set up backup software timer, in case the gen timer fails. + * This could be caused by a hardware reset. + */ +static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_hw *ah = sc->sc_ah; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); + tsf_time -= ath9k_hw_gettsf32(ah); + tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1; + mod_timer(&sc->sched.timer, jiffies + tsf_time); + + ath_dbg(common, CHAN_CTX, + "Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time)); +} + +void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, + enum ath_chanctx_event ev) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_beacon_config *cur_conf; + struct ath_vif *avp = NULL; + struct ath_chanctx *ctx; + u32 tsf_time; + u32 beacon_int; + bool noa_changed = false; + + if (vif) + avp = (struct ath_vif *) vif->drv_priv; + + spin_lock_bh(&sc->chan_lock); + + ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s\n", + sc->cur_chan->chandef.center_freq1, + chanctx_event_string(ev), + chanctx_state_string(sc->sched.state)); + + switch (ev) { + case ATH_CHANCTX_EVENT_BEACON_PREPARE: + if (avp->offchannel_duration) + avp->offchannel_duration = 0; + + if (avp->chanctx != sc->cur_chan) { + ath_dbg(common, CHAN_CTX, + "Contexts differ, not preparing beacon\n"); + break; + } + + if (sc->sched.offchannel_pending) { + sc->sched.offchannel_pending = false; + sc->next_chan = &sc->offchannel.chan; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + ath_dbg(common, CHAN_CTX, + "Setting offchannel_pending to false\n"); + } + + ctx = ath_chanctx_get_next(sc, sc->cur_chan); + if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) { + sc->next_chan = ctx; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + ath_dbg(common, CHAN_CTX, + "Set next context, move chanctx state to WAIT_FOR_BEACON\n"); + } + + /* if the timer missed its window, use the next interval */ + if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) { + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + ath_dbg(common, CHAN_CTX, + "Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n"); + } + + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + ath_dbg(common, CHAN_CTX, "Preparing beacon for vif: %pM\n", vif->addr); + + sc->sched.beacon_pending = true; + sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); + + cur_conf = &sc->cur_chan->beacon; + beacon_int = TU_TO_USEC(cur_conf->beacon_interval); + + /* defer channel switch by a quarter beacon interval */ + tsf_time = sc->sched.next_tbtt + beacon_int / 4; + sc->sched.switch_start_time = tsf_time; + sc->cur_chan->last_beacon = sc->sched.next_tbtt; + + /* Prevent wrap-around issues */ + if (avp->periodic_noa_duration && + tsf_time - avp->periodic_noa_start > BIT(30)) + avp->periodic_noa_duration = 0; + + if (ctx->active && !avp->periodic_noa_duration) { + avp->periodic_noa_start = tsf_time; + avp->periodic_noa_duration = + TU_TO_USEC(cur_conf->beacon_interval) / 2 - + sc->sched.channel_switch_time; + noa_changed = true; + } else if (!ctx->active && avp->periodic_noa_duration) { + avp->periodic_noa_duration = 0; + noa_changed = true; + } + + /* If at least two consecutive beacons were missed on the STA + * chanctx, stay on the STA channel for one extra beacon period, + * to resync the timer properly. + */ + if (ctx->active && sc->sched.beacon_miss >= 2) + sc->sched.offchannel_duration = 3 * beacon_int / 2; + + if (sc->sched.offchannel_duration) { + noa_changed = true; + avp->offchannel_start = tsf_time; + avp->offchannel_duration = + sc->sched.offchannel_duration; + } + + if (noa_changed) + avp->noa_index++; + + ath_dbg(common, CHAN_CTX, + "periodic_noa_duration: %d, periodic_noa_start: %d, noa_index: %d\n", + avp->periodic_noa_duration, + avp->periodic_noa_start, + avp->noa_index); + + break; + case ATH_CHANCTX_EVENT_BEACON_SENT: + if (!sc->sched.beacon_pending) { + ath_dbg(common, CHAN_CTX, + "No pending beacon\n"); + break; + } + + sc->sched.beacon_pending = false; + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + ath_dbg(common, CHAN_CTX, + "Move chanctx state to WAIT_FOR_TIMER\n"); + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + ath_chanctx_setup_timer(sc, sc->sched.switch_start_time); + break; + case ATH_CHANCTX_EVENT_TSF_TIMER: + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) + break; + + if (!sc->cur_chan->switch_after_beacon && + sc->sched.beacon_pending) + sc->sched.beacon_miss++; + + ath_dbg(common, CHAN_CTX, + "Move chanctx state to SWITCH\n"); + + sc->sched.state = ATH_CHANCTX_STATE_SWITCH; + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + case ATH_CHANCTX_EVENT_BEACON_RECEIVED: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->cur_chan == &sc->offchannel.chan) + break; + + ath_chanctx_adjust_tbtt_delta(sc); + sc->sched.beacon_pending = false; + sc->sched.beacon_miss = 0; + + /* TSF time might have been updated by the incoming beacon, + * need update the channel switch timer to reflect the change. + */ + tsf_time = sc->sched.switch_start_time; + tsf_time -= (u32) sc->cur_chan->tsf_val + + ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); + tsf_time += ath9k_hw_gettsf32(ah); + + + ath_chanctx_setup_timer(sc, tsf_time); + break; + case ATH_CHANCTX_EVENT_ASSOC: + if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || + avp->chanctx != sc->cur_chan) + break; + + ath_dbg(common, CHAN_CTX, + "Move chanctx state from FORCE_ACTIVE to IDLE\n"); + + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + /* fall through */ + case ATH_CHANCTX_EVENT_SWITCH: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || + sc->cur_chan->switch_after_beacon || + sc->cur_chan == &sc->offchannel.chan) + break; + + /* If this is a station chanctx, stay active for a half + * beacon period (minus channel switch time) + */ + sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + cur_conf = &sc->cur_chan->beacon; + + ath_dbg(common, CHAN_CTX, + "Move chanctx state to WAIT_FOR_TIMER (event SWITCH)\n"); + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + + tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2; + if (sc->sched.beacon_miss >= 2) { + sc->sched.beacon_miss = 0; + tsf_time *= 3; + } + + tsf_time -= sc->sched.channel_switch_time; + tsf_time += ath9k_hw_gettsf32(sc->sc_ah); + sc->sched.switch_start_time = tsf_time; + + ath_chanctx_setup_timer(sc, tsf_time); + sc->sched.beacon_pending = true; + break; + case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: + if (sc->cur_chan == &sc->offchannel.chan || + sc->cur_chan->switch_after_beacon) + break; + + sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + case ATH_CHANCTX_EVENT_UNASSIGN: + if (sc->cur_chan->assigned) { + if (sc->next_chan && !sc->next_chan->assigned && + sc->next_chan != &sc->offchannel.chan) + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + break; + } + + ctx = ath_chanctx_get_next(sc, sc->cur_chan); + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + if (!ctx->assigned) + break; + + sc->next_chan = ctx; + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + case ATH_CHANCTX_EVENT_ASSIGN: + /* + * When adding a new channel context, check if a scan + * is in progress and abort it since the addition of + * a new channel context is usually followed by VIF + * assignment, in which case we have to start multi-channel + * operation. + */ + if (test_bit(ATH_OP_SCANNING, &common->op_flags)) { + ath_dbg(common, CHAN_CTX, + "Aborting HW scan to add new context\n"); + + spin_unlock_bh(&sc->chan_lock); + del_timer_sync(&sc->offchannel.timer); + ath_scan_complete(sc, true); + spin_lock_bh(&sc->chan_lock); + } + break; + case ATH_CHANCTX_EVENT_CHANGE: + break; + } + + spin_unlock_bh(&sc->chan_lock); +} + +void ath_chanctx_beacon_sent_ev(struct ath_softc *sc, + enum ath_chanctx_event ev) +{ + if (sc->sched.beacon_pending) + ath_chanctx_event(sc, NULL, ev); +} + +void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, u32 ts, + enum ath_chanctx_event ev) +{ + sc->sched.next_tbtt = ts; + ath_chanctx_event(sc, NULL, ev); +} + +static int ath_scan_channel_duration(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + + if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR)) + return (HZ / 9); /* ~110 ms */ + + return (HZ / 16); /* ~60 ms */ +} + +static void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + spin_lock_bh(&sc->chan_lock); + + if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) && + (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) { + sc->sched.offchannel_pending = true; + spin_unlock_bh(&sc->chan_lock); + return; + } + + sc->next_chan = ctx; + if (chandef) { + ctx->chandef = *chandef; + ath_dbg(common, CHAN_CTX, + "Assigned next_chan to %d MHz\n", chandef->center_freq1); + } + + if (sc->next_chan == &sc->offchannel.chan) { + sc->sched.offchannel_duration = + TU_TO_USEC(sc->offchannel.duration) + + sc->sched.channel_switch_time; + + if (chandef) { + ath_dbg(common, CHAN_CTX, + "Offchannel duration for chan %d MHz : %u\n", + chandef->center_freq1, + sc->sched.offchannel_duration); + } + } + spin_unlock_bh(&sc->chan_lock); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); +} + +static void ath_chanctx_offchan_switch(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct cfg80211_chan_def chandef; + + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + ath_dbg(common, CHAN_CTX, + "Channel definition created: %d MHz\n", chandef.center_freq1); + + ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); +} + +static struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, + bool active) +{ + struct ath_chanctx *ctx; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + if (active && !ctx->active) + continue; + + if (ctx->switch_after_beacon) + return ctx; + } + + return &sc->chanctx[0]; +} + +static void +ath_scan_next_channel(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_channel *chan; + + if (sc->offchannel.scan_idx >= req->n_channels) { + ath_dbg(common, CHAN_CTX, + "Moving offchannel state to ATH_OFFCHANNEL_IDLE, " + "scan_idx: %d, n_channels: %d\n", + sc->offchannel.scan_idx, + req->n_channels); + + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), + NULL); + return; + } + + ath_dbg(common, CHAN_CTX, + "Moving offchannel state to ATH_OFFCHANNEL_PROBE_SEND, scan_idx: %d\n", + sc->offchannel.scan_idx); + + chan = req->channels[sc->offchannel.scan_idx++]; + sc->offchannel.duration = ath_scan_channel_duration(sc, chan); + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND; + + ath_chanctx_offchan_switch(sc, chan); +} + +void ath_offchannel_next(struct ath_softc *sc) +{ + struct ieee80211_vif *vif; + + if (sc->offchannel.scan_req) { + vif = sc->offchannel.scan_vif; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + ath_scan_next_channel(sc); + } else if (sc->offchannel.roc_vif) { + vif = sc->offchannel.roc_vif; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + sc->offchannel.duration = sc->offchannel.roc_duration; + sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; + ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); + } else { + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), + NULL); + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + if (sc->ps_idle) + ath_cancel_work(sc); + } +} + +void ath_roc_complete(struct ath_softc *sc, bool abort) +{ + sc->offchannel.roc_vif = NULL; + sc->offchannel.roc_chan = NULL; + if (!abort) + ieee80211_remain_on_channel_expired(sc->hw); + ath_offchannel_next(sc); + ath9k_ps_restore(sc); +} + +void ath_scan_complete(struct ath_softc *sc, bool abort) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + if (abort) + ath_dbg(common, CHAN_CTX, "HW scan aborted\n"); + else + ath_dbg(common, CHAN_CTX, "HW scan complete\n"); + + sc->offchannel.scan_req = NULL; + sc->offchannel.scan_vif = NULL; + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + ieee80211_scan_completed(sc->hw, abort); + clear_bit(ATH_OP_SCANNING, &common->op_flags); + ath_offchannel_next(sc); + ath9k_ps_restore(sc); +} + +static void ath_scan_send_probe(struct ath_softc *sc, + struct cfg80211_ssid *ssid) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_vif *vif = sc->offchannel.scan_vif; + struct ath_tx_control txctl = {}; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + int band = sc->offchannel.chan.chandef.chan->band; + + skb = ieee80211_probereq_get(sc->hw, vif, + ssid->ssid, ssid->ssid_len, req->ie_len); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + if (req->no_cck) + info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + + if (req->ie_len) + memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + + if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL)) + goto error; + + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + txctl.force_channel = true; + if (ath_tx_start(sc->hw, skb, &txctl)) + goto error; + + return; + +error: + ieee80211_free_txskb(sc->hw, skb); +} + +static void ath_scan_channel_start(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + int i; + + if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) && + req->n_ssids) { + for (i = 0; i < req->n_ssids; i++) + ath_scan_send_probe(sc, &req->ssids[i]); + + } + + ath_dbg(common, CHAN_CTX, + "Moving offchannel state to ATH_OFFCHANNEL_PROBE_WAIT\n"); + + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration); +} + +static void ath_chanctx_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, + "Channel context timer invoked\n"); + + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); +} + +static void ath_offchannel_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_chanctx *ctx; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n", + __func__, offchannel_state_string(sc->offchannel.state)); + + switch (sc->offchannel.state) { + case ATH_OFFCHANNEL_PROBE_WAIT: + if (!sc->offchannel.scan_req) + return; + + /* get first active channel context */ + ctx = ath_chanctx_get_oper_chan(sc, true); + if (ctx->active) { + ath_dbg(common, CHAN_CTX, + "Switch to oper/active context, " + "move offchannel state to ATH_OFFCHANNEL_SUSPEND\n"); + + sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; + ath_chanctx_switch(sc, ctx, NULL); + mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); + break; + } + /* fall through */ + case ATH_OFFCHANNEL_SUSPEND: + if (!sc->offchannel.scan_req) + return; + + ath_scan_next_channel(sc); + break; + case ATH_OFFCHANNEL_ROC_START: + case ATH_OFFCHANNEL_ROC_WAIT: + ctx = ath_chanctx_get_oper_chan(sc, false); + sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; + ath_chanctx_switch(sc, ctx, NULL); + break; + default: + break; + } +} + static bool ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, bool powersave) @@ -148,47 +888,6 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, return true; } -void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp; - bool active = false; - u8 n_active = 0; - - if (!ctx) - return; - - list_for_each_entry(avp, &ctx->vifs, list) { - struct ieee80211_vif *vif = avp->vif; - - switch (vif->type) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - if (vif->bss_conf.assoc) - active = true; - break; - default: - active = true; - break; - } - } - ctx->active = active; - - ath_for_each_chanctx(sc, ctx) { - if (!ctx->assigned || list_empty(&ctx->vifs)) - continue; - n_active++; - } - - if (n_active <= 1) { - clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); - return; - } - if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) - return; - ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); -} - static bool ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) { @@ -207,6 +906,8 @@ ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) static bool ath_chanctx_defer_switch(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + if (sc->cur_chan == &sc->offchannel.chan) return false; @@ -217,6 +918,9 @@ static bool ath_chanctx_defer_switch(struct ath_softc *sc) if (!sc->cur_chan->switch_after_beacon) return false; + ath_dbg(common, CHAN_CTX, + "Defer switch, set chanctx state to WAIT_FOR_BEACON\n"); + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; break; default: @@ -226,8 +930,50 @@ static bool ath_chanctx_defer_switch(struct ath_softc *sc) return true; } -static void ath_chanctx_set_next(struct ath_softc *sc, bool force) +static void ath_offchannel_channel_change(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n", + __func__, offchannel_state_string(sc->offchannel.state)); + + switch (sc->offchannel.state) { + case ATH_OFFCHANNEL_PROBE_SEND: + if (!sc->offchannel.scan_req) + return; + + if (sc->cur_chan->chandef.chan != + sc->offchannel.chan.chandef.chan) + return; + + ath_scan_channel_start(sc); + break; + case ATH_OFFCHANNEL_IDLE: + if (!sc->offchannel.scan_req) + return; + + ath_scan_complete(sc, false); + break; + case ATH_OFFCHANNEL_ROC_START: + if (sc->cur_chan != &sc->offchannel.chan) + break; + + sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + + msecs_to_jiffies(sc->offchannel.duration)); + ieee80211_ready_on_channel(sc->hw); + break; + case ATH_OFFCHANNEL_ROC_DONE: + ath_roc_complete(sc, false); + break; + default: + break; + } +} + +void ath_chanctx_set_next(struct ath_softc *sc, bool force) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct timespec ts; bool measure_time = false; bool send_ps = false; @@ -243,7 +989,16 @@ static void ath_chanctx_set_next(struct ath_softc *sc, bool force) return; } + ath_dbg(common, CHAN_CTX, + "%s: current: %d MHz, next: %d MHz\n", + __func__, + sc->cur_chan->chandef.center_freq1, + sc->next_chan->chandef.center_freq1); + if (sc->cur_chan != sc->next_chan) { + ath_dbg(common, CHAN_CTX, + "Stopping current chanctx: %d\n", + sc->cur_chan->chandef.center_freq1); sc->cur_chan->stopped = true; spin_unlock_bh(&sc->chan_lock); @@ -276,6 +1031,9 @@ static void ath_chanctx_set_next(struct ath_softc *sc, bool force) if (sc->sc_ah->chip_fullsleep || memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, sizeof(sc->cur_chandef))) { + ath_dbg(common, CHAN_CTX, + "%s: Set channel %d MHz\n", + __func__, sc->cur_chan->chandef.center_freq1); ath_set_channel(sc); if (measure_time) sc->sched.channel_switch_time = @@ -288,7 +1046,7 @@ static void ath_chanctx_set_next(struct ath_softc *sc, bool force) ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); } -void ath_chanctx_work(struct work_struct *work) +static void ath_chanctx_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, chanctx_work); @@ -297,389 +1055,258 @@ void ath_chanctx_work(struct work_struct *work) mutex_unlock(&sc->mutex); } -void ath_chanctx_init(struct ath_softc *sc) +void ath9k_offchannel_init(struct ath_softc *sc) { struct ath_chanctx *ctx; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - int i, j; + int i; sband = &common->sbands[IEEE80211_BAND_2GHZ]; if (!sband->n_channels) sband = &common->sbands[IEEE80211_BAND_5GHZ]; chan = &sband->channels[0]; - for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { - ctx = &sc->chanctx[i]; - cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); - INIT_LIST_HEAD(&ctx->vifs); - ctx->txpower = ATH_TXPOWER_MAX; - for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) - INIT_LIST_HEAD(&ctx->acq[j]); - } + ctx = &sc->offchannel.chan; - cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); INIT_LIST_HEAD(&ctx->vifs); ctx->txpower = ATH_TXPOWER_MAX; - for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) - INIT_LIST_HEAD(&ctx->acq[j]); - sc->offchannel.chan.offchannel = true; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) + INIT_LIST_HEAD(&ctx->acq[i]); + + sc->offchannel.chan.offchannel = true; } -void ath9k_chanctx_force_active(struct ieee80211_hw *hw, +void ath9k_init_channel_context(struct ath_softc *sc) +{ + INIT_WORK(&sc->chanctx_work, ath_chanctx_work); + + setup_timer(&sc->offchannel.timer, ath_offchannel_timer, + (unsigned long)sc); + setup_timer(&sc->sched.timer, ath_chanctx_timer, + (unsigned long)sc); +} + +void ath9k_deinit_channel_context(struct ath_softc *sc) +{ + cancel_work_sync(&sc->chanctx_work); +} + +bool ath9k_is_chanctx_enabled(void) +{ + return (ath9k_use_chanctx == 1); +} + +/********************/ +/* Queue management */ +/********************/ + +void ath9k_chanctx_wake_queues(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + int i; + + if (sc->cur_chan == &sc->offchannel.chan) { + ieee80211_wake_queue(sc->hw, + sc->hw->offchannel_tx_hw_queue); + } else { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + ieee80211_wake_queue(sc->hw, + sc->cur_chan->hw_queue_base + i); + } + + if (ah->opmode == NL80211_IFTYPE_AP) + ieee80211_wake_queue(sc->hw, sc->hw->queues - 2); +} + +/*****************/ +/* P2P Powersave */ +/*****************/ + +static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) +{ + struct ath_hw *ah = sc->sc_ah; + s32 tsf, target_tsf; + + if (!avp || !avp->noa.has_next_tsf) + return; + + ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer); + + tsf = ath9k_hw_gettsf32(sc->sc_ah); + + target_tsf = avp->noa.next_tsf; + if (!avp->noa.absent) + target_tsf -= ATH_P2P_PS_STOP_TIME; + + if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) + target_tsf = tsf + ATH_P2P_PS_STOP_TIME; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); +} + +static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) +{ + struct ath_vif *avp = (void *)vif->drv_priv; + u32 tsf; + + if (!sc->p2p_ps_timer) + return; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p) + return; + + sc->p2p_ps_vif = avp; + tsf = ath9k_hw_gettsf32(sc->sc_ah); + ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); + ath9k_update_p2p_ps_timer(sc, avp); +} + +void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb) +{ + static const u8 noa_ie_hdr[] = { + WLAN_EID_VENDOR_SPECIFIC, /* type */ + 0, /* length */ + 0x50, 0x6f, 0x9a, /* WFA OUI */ + 0x09, /* P2P subtype */ + 0x0c, /* Notice of Absence */ + 0x00, /* LSB of little-endian len */ + 0x00, /* MSB of little-endian len */ + }; + + struct ieee80211_p2p_noa_attr *noa; + int noa_len, noa_desc, i = 0; + u8 *hdr; + + if (!avp->offchannel_duration && !avp->periodic_noa_duration) + return; + + noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration; + noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc; + + hdr = skb_put(skb, sizeof(noa_ie_hdr)); + memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr)); + hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2; + hdr[7] = noa_len; + + noa = (void *) skb_put(skb, noa_len); + memset(noa, 0, noa_len); + + noa->index = avp->noa_index; + if (avp->periodic_noa_duration) { + u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + + noa->desc[i].count = 255; + noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start); + noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration); + noa->desc[i].interval = cpu_to_le32(interval); + i++; + } + + if (avp->offchannel_duration) { + noa->desc[i].count = 1; + noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start); + noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration); + } +} + +void ath9k_p2p_ps_timer(void *priv) +{ + struct ath_softc *sc = priv; + struct ath_vif *avp = sc->p2p_ps_vif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct ath_node *an; + u32 tsf; + + del_timer_sync(&sc->sched.timer); + ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); + + if (!avp || avp->chanctx != sc->cur_chan) + return; + + tsf = ath9k_hw_gettsf32(sc->sc_ah); + if (!avp->noa.absent) + tsf += ATH_P2P_PS_STOP_TIME; + + if (!avp->noa.has_next_tsf || + avp->noa.next_tsf - tsf > BIT(31)) + ieee80211_update_p2p_noa(&avp->noa, tsf); + + ath9k_update_p2p_ps_timer(sc, avp); + + rcu_read_lock(); + + vif = avp->vif; + sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (!sta) + goto out; + + an = (void *) sta->drv_priv; + if (an->sleeping == !!avp->noa.absent) + goto out; + + an->sleeping = avp->noa.absent; + if (an->sleeping) + ath_tx_aggr_sleep(sta, sc, an); + else + ath_tx_aggr_wakeup(sc, an); + +out: + rcu_read_unlock(); +} + +void ath9k_p2p_bss_info_changed(struct ath_softc *sc, struct ieee80211_vif *vif) { - struct ath_softc *sc = hw->priv; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (struct ath_vif *) vif->drv_priv; - bool changed = false; + unsigned long flags; - if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) - return; + spin_lock_bh(&sc->sc_pcu_lock); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (!(sc->ps_flags & PS_BEACON_SYNC)) + ath9k_update_p2p_ps(sc, vif); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + spin_unlock_bh(&sc->sc_pcu_lock); +} - if (!avp->chanctx) - return; +void ath9k_p2p_beacon_sync(struct ath_softc *sc) +{ + if (sc->p2p_ps_vif) + ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); +} - mutex_lock(&sc->mutex); +void ath9k_p2p_remove_vif(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ + struct ath_vif *avp = (void *)vif->drv_priv; - spin_lock_bh(&sc->chan_lock); - if (sc->next_chan || (sc->cur_chan != avp->chanctx)) { - sc->next_chan = avp->chanctx; - changed = true; + spin_lock_bh(&sc->sc_pcu_lock); + if (avp == sc->p2p_ps_vif) { + sc->p2p_ps_vif = NULL; + ath9k_update_p2p_ps_timer(sc, NULL); } - sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE; - spin_unlock_bh(&sc->chan_lock); - - if (changed) - ath_chanctx_set_next(sc, true); - - mutex_unlock(&sc->mutex); + spin_unlock_bh(&sc->sc_pcu_lock); } -void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, - struct cfg80211_chan_def *chandef) +int ath9k_init_p2p(struct ath_softc *sc) { - struct ath_common *common = ath9k_hw_common(sc->sc_ah); + sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer, + NULL, sc, AR_FIRST_NDP_TIMER); + if (!sc->p2p_ps_timer) + return -ENOMEM; - spin_lock_bh(&sc->chan_lock); - - if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) && - (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) { - sc->sched.offchannel_pending = true; - spin_unlock_bh(&sc->chan_lock); - return; - } - - sc->next_chan = ctx; - if (chandef) - ctx->chandef = *chandef; - - if (sc->next_chan == &sc->offchannel.chan) { - sc->sched.offchannel_duration = - TU_TO_USEC(sc->offchannel.duration) + - sc->sched.channel_switch_time; - } - spin_unlock_bh(&sc->chan_lock); - ieee80211_queue_work(sc->hw, &sc->chanctx_work); + return 0; } -void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, - struct cfg80211_chan_def *chandef) +void ath9k_deinit_p2p(struct ath_softc *sc) { - bool cur_chan; - - spin_lock_bh(&sc->chan_lock); - if (chandef) - memcpy(&ctx->chandef, chandef, sizeof(*chandef)); - cur_chan = sc->cur_chan == ctx; - spin_unlock_bh(&sc->chan_lock); - - if (!cur_chan) - return; - - ath_set_channel(sc); + if (sc->p2p_ps_timer) + ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer); } -struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active) -{ - struct ath_chanctx *ctx; - - ath_for_each_chanctx(sc, ctx) { - if (!ctx->assigned || list_empty(&ctx->vifs)) - continue; - if (active && !ctx->active) - continue; - - if (ctx->switch_after_beacon) - return ctx; - } - - return &sc->chanctx[0]; -} - -void ath_chanctx_offchan_switch(struct ath_softc *sc, - struct ieee80211_channel *chan) -{ - struct cfg80211_chan_def chandef; - - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - - ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); -} - -static struct ath_chanctx * -ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx) -{ - int idx = ctx - &sc->chanctx[0]; - - return &sc->chanctx[!idx]; -} - -static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) -{ - struct ath_chanctx *prev, *cur; - struct timespec ts; - u32 cur_tsf, prev_tsf, beacon_int; - s32 offset; - - beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); - - cur = sc->cur_chan; - prev = ath_chanctx_get_next(sc, cur); - - getrawmonotonic(&ts); - cur_tsf = (u32) cur->tsf_val + - ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); - - prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf; - prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts); - - /* Adjust the TSF time of the AP chanctx to keep its beacons - * at half beacon interval offset relative to the STA chanctx. - */ - offset = cur_tsf - prev_tsf; - - /* Ignore stale data or spurious timestamps */ - if (offset < 0 || offset > 3 * beacon_int) - return; - - offset = beacon_int / 2 - (offset % beacon_int); - prev->tsf_val += offset; -} - -void ath_chanctx_timer(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *) data; - - ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); -} - -/* Configure the TSF based hardware timer for a channel switch. - * Also set up backup software timer, in case the gen timer fails. - * This could be caused by a hardware reset. - */ -static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) -{ - struct ath_hw *ah = sc->sc_ah; - - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); - tsf_time -= ath9k_hw_gettsf32(ah); - tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1; - mod_timer(&sc->sched.timer, tsf_time); -} - -void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, - enum ath_chanctx_event ev) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_beacon_config *cur_conf; - struct ath_vif *avp = NULL; - struct ath_chanctx *ctx; - u32 tsf_time; - u32 beacon_int; - bool noa_changed = false; - - if (vif) - avp = (struct ath_vif *) vif->drv_priv; - - spin_lock_bh(&sc->chan_lock); - - switch (ev) { - case ATH_CHANCTX_EVENT_BEACON_PREPARE: - if (avp->offchannel_duration) - avp->offchannel_duration = 0; - - if (avp->chanctx != sc->cur_chan) - break; - - if (sc->sched.offchannel_pending) { - sc->sched.offchannel_pending = false; - sc->next_chan = &sc->offchannel.chan; - sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; - } - - ctx = ath_chanctx_get_next(sc, sc->cur_chan); - if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) { - sc->next_chan = ctx; - sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; - } - - /* if the timer missed its window, use the next interval */ - if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) - sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; - - if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) - break; - - sc->sched.beacon_pending = true; - sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); - - cur_conf = &sc->cur_chan->beacon; - beacon_int = TU_TO_USEC(cur_conf->beacon_interval); - - /* defer channel switch by a quarter beacon interval */ - tsf_time = sc->sched.next_tbtt + beacon_int / 4; - sc->sched.switch_start_time = tsf_time; - sc->cur_chan->last_beacon = sc->sched.next_tbtt; - - /* Prevent wrap-around issues */ - if (avp->periodic_noa_duration && - tsf_time - avp->periodic_noa_start > BIT(30)) - avp->periodic_noa_duration = 0; - - if (ctx->active && !avp->periodic_noa_duration) { - avp->periodic_noa_start = tsf_time; - avp->periodic_noa_duration = - TU_TO_USEC(cur_conf->beacon_interval) / 2 - - sc->sched.channel_switch_time; - noa_changed = true; - } else if (!ctx->active && avp->periodic_noa_duration) { - avp->periodic_noa_duration = 0; - noa_changed = true; - } - - /* If at least two consecutive beacons were missed on the STA - * chanctx, stay on the STA channel for one extra beacon period, - * to resync the timer properly. - */ - if (ctx->active && sc->sched.beacon_miss >= 2) - sc->sched.offchannel_duration = 3 * beacon_int / 2; - - if (sc->sched.offchannel_duration) { - noa_changed = true; - avp->offchannel_start = tsf_time; - avp->offchannel_duration = - sc->sched.offchannel_duration; - } - - if (noa_changed) - avp->noa_index++; - break; - case ATH_CHANCTX_EVENT_BEACON_SENT: - if (!sc->sched.beacon_pending) - break; - - sc->sched.beacon_pending = false; - if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) - break; - - sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; - ath_chanctx_setup_timer(sc, sc->sched.switch_start_time); - break; - case ATH_CHANCTX_EVENT_TSF_TIMER: - if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) - break; - - if (!sc->cur_chan->switch_after_beacon && - sc->sched.beacon_pending) - sc->sched.beacon_miss++; - - sc->sched.state = ATH_CHANCTX_STATE_SWITCH; - ieee80211_queue_work(sc->hw, &sc->chanctx_work); - break; - case ATH_CHANCTX_EVENT_BEACON_RECEIVED: - if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || - sc->cur_chan == &sc->offchannel.chan) - break; - - ath_chanctx_adjust_tbtt_delta(sc); - sc->sched.beacon_pending = false; - sc->sched.beacon_miss = 0; - - /* TSF time might have been updated by the incoming beacon, - * need update the channel switch timer to reflect the change. - */ - tsf_time = sc->sched.switch_start_time; - tsf_time -= (u32) sc->cur_chan->tsf_val + - ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); - tsf_time += ath9k_hw_gettsf32(ah); - - - ath_chanctx_setup_timer(sc, tsf_time); - break; - case ATH_CHANCTX_EVENT_ASSOC: - if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || - avp->chanctx != sc->cur_chan) - break; - - sc->sched.state = ATH_CHANCTX_STATE_IDLE; - /* fall through */ - case ATH_CHANCTX_EVENT_SWITCH: - if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || - sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || - sc->cur_chan->switch_after_beacon || - sc->cur_chan == &sc->offchannel.chan) - break; - - /* If this is a station chanctx, stay active for a half - * beacon period (minus channel switch time) - */ - sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); - cur_conf = &sc->cur_chan->beacon; - - sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; - - tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2; - if (sc->sched.beacon_miss >= 2) { - sc->sched.beacon_miss = 0; - tsf_time *= 3; - } - - tsf_time -= sc->sched.channel_switch_time; - tsf_time += ath9k_hw_gettsf32(sc->sc_ah); - sc->sched.switch_start_time = tsf_time; - - ath_chanctx_setup_timer(sc, tsf_time); - sc->sched.beacon_pending = true; - break; - case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: - if (sc->cur_chan == &sc->offchannel.chan || - sc->cur_chan->switch_after_beacon) - break; - - sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); - ieee80211_queue_work(sc->hw, &sc->chanctx_work); - break; - case ATH_CHANCTX_EVENT_UNASSIGN: - if (sc->cur_chan->assigned) { - if (sc->next_chan && !sc->next_chan->assigned && - sc->next_chan != &sc->offchannel.chan) - sc->sched.state = ATH_CHANCTX_STATE_IDLE; - break; - } - - ctx = ath_chanctx_get_next(sc, sc->cur_chan); - sc->sched.state = ATH_CHANCTX_STATE_IDLE; - if (!ctx->assigned) - break; - - sc->next_chan = ctx; - ieee80211_queue_work(sc->hw, &sc->chanctx_work); - break; - } - - spin_unlock_bh(&sc->chan_lock); -} +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 8a3bd5fe3a54..d779f4fa50e3 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -592,6 +592,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + hw->queues = 4; hw->max_listen_interval = 1; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 39419ea845cc..ca10a8b3a381 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -61,10 +61,14 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + int ath9k_use_chanctx; module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ + bool is_ath9k_unloaded; #ifdef CONFIG_MAC80211_LEDS @@ -511,7 +515,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); sc->cur_chan = &sc->chanctx[0]; - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) sc->cur_chan->hw_queue_base = 0; if (!pdata || pdata->use_eeprom) { @@ -567,11 +571,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc); INIT_WORK(&sc->hw_reset_work, ath_reset_work); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); - INIT_WORK(&sc->chanctx_work, ath_chanctx_work); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); - setup_timer(&sc->offchannel.timer, ath_offchannel_timer, - (unsigned long)sc); - setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc); + + ath9k_init_channel_context(sc); /* * Cache line size is used to size and align various @@ -600,13 +602,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (ret) goto err_btcoex; - sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer, - NULL, sc, AR_FIRST_NDP_TIMER); + ret = ath9k_init_p2p(sc); + if (ret) + goto err_btcoex; ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); ath_fill_led_pin(sc); ath_chanctx_init(sc); + ath9k_offchannel_init(sc); if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); @@ -672,18 +676,14 @@ static const struct ieee80211_iface_limit wds_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, }; -static const struct ieee80211_iface_limit if_limits_multi[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, - { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) }, -}; +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT -static const struct ieee80211_iface_limit if_dfs_limits[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_ADHOC) }, +static const struct ieee80211_iface_limit if_limits_multi[] = { + { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, }; static const struct ieee80211_iface_combination if_comb_multi[] = { @@ -696,6 +696,16 @@ static const struct ieee80211_iface_combination if_comb_multi[] = { }, }; +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ + +static const struct ieee80211_iface_limit if_dfs_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_ADHOC) }, +}; + static const struct ieee80211_iface_combination if_comb[] = { { .limits = if_limits, @@ -763,24 +773,31 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - if (!ath9k_use_chanctx) { + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_WDS); + hw->wiphy->iface_combinations = if_comb; hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); - } else { - hw->wiphy->iface_combinations = if_comb_multi; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(if_comb_multi); - hw->wiphy->max_scan_ssids = 255; - hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; - hw->wiphy->max_remain_on_channel_duration = 10000; - hw->chanctx_data_size = sizeof(void *); - hw->extra_beacon_tailroom = - sizeof(struct ieee80211_p2p_noa_attr) + 9; - } } +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + + if (ath9k_is_chanctx_enabled()) { + hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS); + hw->wiphy->iface_combinations = if_comb_multi; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi); + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 10000; + hw->chanctx_data_size = sizeof(void *); + hw->extra_beacon_tailroom = + sizeof(struct ieee80211_p2p_noa_attr) + 9; + + ath_dbg(common, CHAN_CTX, "Use channel contexts\n"); + } + +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -915,9 +932,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; - if (sc->p2p_ps_timer) - ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer); - + ath9k_deinit_p2p(sc); ath9k_deinit_btcoex(sc); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e6ac8d2e610c..d9be83158a82 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -223,7 +223,6 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; - int i; if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); @@ -268,20 +267,10 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) ieee80211_wake_queues(sc->hw); - else { - if (sc->cur_chan == &sc->offchannel.chan) - ieee80211_wake_queue(sc->hw, - sc->hw->offchannel_tx_hw_queue); - else { - for (i = 0; i < IEEE80211_NUM_ACS; i++) - ieee80211_wake_queue(sc->hw, - sc->cur_chan->hw_queue_base + i); - } - if (ah->opmode == NL80211_IFTYPE_AP) - ieee80211_wake_queue(sc->hw, sc->hw->queues - 2); - } + else + ath9k_chanctx_wake_queues(sc); ath9k_p2p_ps_timer(sc); @@ -314,6 +303,9 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!ath_prepare_reset(sc)) fastcc = false; + if (ath9k_is_chanctx_enabled()) + fastcc = false; + spin_lock_bh(&sc->chan_lock); sc->cur_chandef = sc->cur_chan->chandef; spin_unlock_bh(&sc->chan_lock); @@ -822,7 +814,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); bool prev_idle; - cancel_work_sync(&sc->chanctx_work); + ath9k_deinit_channel_context(sc); + mutex_lock(&sc->mutex); ath_cancel_work(sc); @@ -903,9 +896,9 @@ static bool ath9k_uses_beacons(int type) } } -static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, + u8 *mac, struct ieee80211_vif *vif) { - struct ath9k_vif_iter_data *iter_data = data; int i; if (iter_data->has_hw_macaddr) { @@ -968,6 +961,7 @@ void ath9k_calculate_iter_data(struct ath_softc *sc, list_for_each_entry(avp, &ctx->vifs, list) ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif); +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT if (ctx == &sc->offchannel.chan) { struct ieee80211_vif *vif; @@ -980,6 +974,7 @@ void ath9k_calculate_iter_data(struct ath_softc *sc, ath9k_vif_iter(iter_data, vif->addr, vif); iter_data->beacons = false; } +#endif } static void ath9k_set_assoc_state(struct ath_softc *sc, @@ -1139,7 +1134,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ath9k_beacon_assign_slot(sc, vif); avp->vif = vif; - if (!ath9k_use_chanctx) { + if (!ath9k_is_chanctx_enabled()) { avp->chanctx = sc->cur_chan; list_add_tail(&avp->list, &avp->chanctx->vifs); } @@ -1202,29 +1197,6 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, return 0; } -static void -ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) -{ - struct ath_hw *ah = sc->sc_ah; - s32 tsf, target_tsf; - - if (!avp || !avp->noa.has_next_tsf) - return; - - ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer); - - tsf = ath9k_hw_gettsf32(sc->sc_ah); - - target_tsf = avp->noa.next_tsf; - if (!avp->noa.absent) - target_tsf -= ATH_P2P_PS_STOP_TIME; - - if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) - target_tsf = tsf + ATH_P2P_PS_STOP_TIME; - - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); -} - static void ath9k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1236,16 +1208,11 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); - spin_lock_bh(&sc->sc_pcu_lock); - if (avp == sc->p2p_ps_vif) { - sc->p2p_ps_vif = NULL; - ath9k_update_p2p_ps_timer(sc, NULL); - } - spin_unlock_bh(&sc->sc_pcu_lock); + ath9k_p2p_remove_vif(sc, vif); sc->nvifs--; sc->tx99_vif = NULL; - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) list_del(&avp->list); if (ath9k_uses_beacons(vif->type)) @@ -1423,7 +1390,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } - if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + if (!ath9k_is_chanctx_enabled() && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef); } @@ -1687,70 +1654,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw, return ret; } -void ath9k_p2p_ps_timer(void *priv) -{ - struct ath_softc *sc = priv; - struct ath_vif *avp = sc->p2p_ps_vif; - struct ieee80211_vif *vif; - struct ieee80211_sta *sta; - struct ath_node *an; - u32 tsf; - - del_timer_sync(&sc->sched.timer); - ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); - ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); - - if (!avp || avp->chanctx != sc->cur_chan) - return; - - tsf = ath9k_hw_gettsf32(sc->sc_ah); - if (!avp->noa.absent) - tsf += ATH_P2P_PS_STOP_TIME; - - if (!avp->noa.has_next_tsf || - avp->noa.next_tsf - tsf > BIT(31)) - ieee80211_update_p2p_noa(&avp->noa, tsf); - - ath9k_update_p2p_ps_timer(sc, avp); - - rcu_read_lock(); - - vif = avp->vif; - sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (!sta) - goto out; - - an = (void *) sta->drv_priv; - if (an->sleeping == !!avp->noa.absent) - goto out; - - an->sleeping = avp->noa.absent; - if (an->sleeping) - ath_tx_aggr_sleep(sta, sc, an); - else - ath_tx_aggr_wakeup(sc, an); - -out: - rcu_read_unlock(); -} - -void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) -{ - struct ath_vif *avp = (void *)vif->drv_priv; - u32 tsf; - - if (!sc->p2p_ps_timer) - return; - - if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p) - return; - - sc->p2p_ps_vif = avp; - tsf = ath9k_hw_gettsf32(sc->sc_ah); - ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); - ath9k_update_p2p_ps_timer(sc, avp); -} - static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -1765,7 +1668,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; - unsigned long flags; int slottime; ath9k_ps_wakeup(sc); @@ -1776,8 +1678,12 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid, bss_conf->assoc); ath9k_calculate_summary_state(sc, avp->chanctx); - if (bss_conf->assoc) - ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC); + + if (ath9k_is_chanctx_enabled()) { + if (bss_conf->assoc) + ath_chanctx_event(sc, vif, + ATH_CHANCTX_EVENT_ASSOC); + } } if (changed & BSS_CHANGED_IBSS) { @@ -1814,14 +1720,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } } - if (changed & BSS_CHANGED_P2P_PS) { - spin_lock_bh(&sc->sc_pcu_lock); - spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (!(sc->ps_flags & PS_BEACON_SYNC)) - ath9k_update_p2p_ps(sc, vif); - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); - spin_unlock_bh(&sc->sc_pcu_lock); - } + if (changed & BSS_CHANGED_P2P_PS) + ath9k_p2p_bss_info_changed(sc, vif); if (changed & CHECK_ANI) ath_check_ani(sc); @@ -2207,207 +2107,7 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(ATH_OP_SCANNING, &common->op_flags); } -static int ath_scan_channel_duration(struct ath_softc *sc, - struct ieee80211_channel *chan) -{ - struct cfg80211_scan_request *req = sc->offchannel.scan_req; - - if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR)) - return (HZ / 9); /* ~110 ms */ - - return (HZ / 16); /* ~60 ms */ -} - -static void -ath_scan_next_channel(struct ath_softc *sc) -{ - struct cfg80211_scan_request *req = sc->offchannel.scan_req; - struct ieee80211_channel *chan; - - if (sc->offchannel.scan_idx >= req->n_channels) { - sc->offchannel.state = ATH_OFFCHANNEL_IDLE; - ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), - NULL); - return; - } - - chan = req->channels[sc->offchannel.scan_idx++]; - sc->offchannel.duration = ath_scan_channel_duration(sc, chan); - sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND; - ath_chanctx_offchan_switch(sc, chan); -} - -static void ath_offchannel_next(struct ath_softc *sc) -{ - struct ieee80211_vif *vif; - - if (sc->offchannel.scan_req) { - vif = sc->offchannel.scan_vif; - sc->offchannel.chan.txpower = vif->bss_conf.txpower; - ath_scan_next_channel(sc); - } else if (sc->offchannel.roc_vif) { - vif = sc->offchannel.roc_vif; - sc->offchannel.chan.txpower = vif->bss_conf.txpower; - sc->offchannel.duration = sc->offchannel.roc_duration; - sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; - ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); - } else { - ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), - NULL); - sc->offchannel.state = ATH_OFFCHANNEL_IDLE; - if (sc->ps_idle) - ath_cancel_work(sc); - } -} - -static void ath_roc_complete(struct ath_softc *sc, bool abort) -{ - sc->offchannel.roc_vif = NULL; - sc->offchannel.roc_chan = NULL; - if (!abort) - ieee80211_remain_on_channel_expired(sc->hw); - ath_offchannel_next(sc); - ath9k_ps_restore(sc); -} - -static void ath_scan_complete(struct ath_softc *sc, bool abort) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - - sc->offchannel.scan_req = NULL; - sc->offchannel.scan_vif = NULL; - sc->offchannel.state = ATH_OFFCHANNEL_IDLE; - ieee80211_scan_completed(sc->hw, abort); - clear_bit(ATH_OP_SCANNING, &common->op_flags); - ath_offchannel_next(sc); - ath9k_ps_restore(sc); -} - -static void ath_scan_send_probe(struct ath_softc *sc, - struct cfg80211_ssid *ssid) -{ - struct cfg80211_scan_request *req = sc->offchannel.scan_req; - struct ieee80211_vif *vif = sc->offchannel.scan_vif; - struct ath_tx_control txctl = {}; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - int band = sc->offchannel.chan.chandef.chan->band; - - skb = ieee80211_probereq_get(sc->hw, vif, - ssid->ssid, ssid->ssid_len, req->ie_len); - if (!skb) - return; - - info = IEEE80211_SKB_CB(skb); - if (req->no_cck) - info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; - - if (req->ie_len) - memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); - - skb_set_queue_mapping(skb, IEEE80211_AC_VO); - - if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL)) - goto error; - - txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; - txctl.force_channel = true; - if (ath_tx_start(sc->hw, skb, &txctl)) - goto error; - - return; - -error: - ieee80211_free_txskb(sc->hw, skb); -} - -static void ath_scan_channel_start(struct ath_softc *sc) -{ - struct cfg80211_scan_request *req = sc->offchannel.scan_req; - int i; - - if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) && - req->n_ssids) { - for (i = 0; i < req->n_ssids; i++) - ath_scan_send_probe(sc, &req->ssids[i]); - - } - - sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; - mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration); -} - -void ath_offchannel_channel_change(struct ath_softc *sc) -{ - switch (sc->offchannel.state) { - case ATH_OFFCHANNEL_PROBE_SEND: - if (!sc->offchannel.scan_req) - return; - - if (sc->cur_chan->chandef.chan != - sc->offchannel.chan.chandef.chan) - return; - - ath_scan_channel_start(sc); - break; - case ATH_OFFCHANNEL_IDLE: - if (!sc->offchannel.scan_req) - return; - - ath_scan_complete(sc, false); - break; - case ATH_OFFCHANNEL_ROC_START: - if (sc->cur_chan != &sc->offchannel.chan) - break; - - sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; - mod_timer(&sc->offchannel.timer, jiffies + - msecs_to_jiffies(sc->offchannel.duration)); - ieee80211_ready_on_channel(sc->hw); - break; - case ATH_OFFCHANNEL_ROC_DONE: - ath_roc_complete(sc, false); - break; - default: - break; - } -} - -void ath_offchannel_timer(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *)data; - struct ath_chanctx *ctx; - - switch (sc->offchannel.state) { - case ATH_OFFCHANNEL_PROBE_WAIT: - if (!sc->offchannel.scan_req) - return; - - /* get first active channel context */ - ctx = ath_chanctx_get_oper_chan(sc, true); - if (ctx->active) { - sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; - ath_chanctx_switch(sc, ctx, NULL); - mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); - break; - } - /* fall through */ - case ATH_OFFCHANNEL_SUSPEND: - if (!sc->offchannel.scan_req) - return; - - ath_scan_next_channel(sc); - break; - case ATH_OFFCHANNEL_ROC_START: - case ATH_OFFCHANNEL_ROC_WAIT: - ctx = ath_chanctx_get_oper_chan(sc, false); - sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; - ath_chanctx_switch(sc, ctx, NULL); - break; - default: - break; - } -} +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) @@ -2430,8 +2130,13 @@ static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sc->offchannel.scan_req = req; sc->offchannel.scan_idx = 0; - if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_dbg(common, CHAN_CTX, "HW scan request received on vif: %pM\n", + vif->addr); + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) { + ath_dbg(common, CHAN_CTX, "Starting HW scan\n"); ath_offchannel_next(sc); + } out: mutex_unlock(&sc->mutex); @@ -2443,6 +2148,9 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr); mutex_lock(&sc->mutex); del_timer_sync(&sc->offchannel.timer); @@ -2456,6 +2164,7 @@ static int ath9k_remain_on_channel(struct ieee80211_hw *hw, enum ieee80211_roc_type type) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); int ret = 0; mutex_lock(&sc->mutex); @@ -2470,8 +2179,14 @@ static int ath9k_remain_on_channel(struct ieee80211_hw *hw, sc->offchannel.roc_chan = chan; sc->offchannel.roc_duration = duration; - if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_dbg(common, CHAN_CTX, + "RoC request on vif: %pM, type: %d duration: %d\n", + vif->addr, type, duration); + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) { + ath_dbg(common, CHAN_CTX, "Starting RoC period\n"); ath_offchannel_next(sc); + } out: mutex_unlock(&sc->mutex); @@ -2482,9 +2197,11 @@ static int ath9k_remain_on_channel(struct ieee80211_hw *hw, static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); + ath_dbg(common, CHAN_CTX, "Cancel RoC\n"); del_timer_sync(&sc->offchannel.timer); if (sc->offchannel.roc_vif) { @@ -2501,6 +2218,7 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *ctx, **ptr; int pos; @@ -2515,10 +2233,18 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, ctx->assigned = true; pos = ctx - &sc->chanctx[0]; ctx->hw_queue_base = pos * IEEE80211_NUM_ACS; + + ath_dbg(common, CHAN_CTX, + "Add channel context: %d MHz\n", + conf->def.chan->center_freq); + ath_chanctx_set_channel(sc, ctx, &conf->def); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ASSIGN); + mutex_unlock(&sc->mutex); return 0; } + mutex_unlock(&sc->mutex); return -ENOSPC; } @@ -2528,12 +2254,19 @@ static void ath9k_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *ctx = ath_chanctx_get(conf); mutex_lock(&sc->mutex); + + ath_dbg(common, CHAN_CTX, + "Remove channel context: %d MHz\n", + conf->def.chan->center_freq); + ctx->assigned = false; ctx->hw_queue_base = -1; ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); + mutex_unlock(&sc->mutex); } @@ -2542,9 +2275,13 @@ static void ath9k_change_chanctx(struct ieee80211_hw *hw, u32 changed) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *ctx = ath_chanctx_get(conf); mutex_lock(&sc->mutex); + ath_dbg(common, CHAN_CTX, + "Change channel context: %d MHz\n", + conf->def.chan->center_freq); ath_chanctx_set_channel(sc, ctx, &conf->def); mutex_unlock(&sc->mutex); } @@ -2554,16 +2291,24 @@ static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_chanctx *ctx = ath_chanctx_get(conf); int i; mutex_lock(&sc->mutex); + + ath_dbg(common, CHAN_CTX, + "Assign VIF (addr: %pM, type: %d, p2p: %d) to channel context: %d MHz\n", + vif->addr, vif->type, vif->p2p, + conf->def.chan->center_freq); + avp->chanctx = ctx; list_add_tail(&avp->list, &ctx->vifs); ath9k_calculate_summary_state(sc, ctx); for (i = 0; i < IEEE80211_NUM_ACS; i++) vif->hw_queue[i] = ctx->hw_queue_base + i; + mutex_unlock(&sc->mutex); return 0; @@ -2574,36 +2319,79 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_chanctx *ctx = ath_chanctx_get(conf); int ac; mutex_lock(&sc->mutex); + + ath_dbg(common, CHAN_CTX, + "Remove VIF (addr: %pM, type: %d, p2p: %d) from channel context: %d MHz\n", + vif->addr, vif->type, vif->p2p, + conf->def.chan->center_freq); + avp->chanctx = NULL; list_del(&avp->list); ath9k_calculate_summary_state(sc, ctx); for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; + + mutex_unlock(&sc->mutex); +} + +static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (struct ath_vif *) vif->drv_priv; + bool changed = false; + + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + return; + + if (!avp->chanctx) + return; + + mutex_lock(&sc->mutex); + + spin_lock_bh(&sc->chan_lock); + if (sc->next_chan || (sc->cur_chan != avp->chanctx)) { + sc->next_chan = avp->chanctx; + changed = true; + } + ath_dbg(common, CHAN_CTX, + "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n", + __func__, changed); + sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE; + spin_unlock_bh(&sc->chan_lock); + + if (changed) + ath_chanctx_set_next(sc, true); + mutex_unlock(&sc->mutex); } void ath9k_fill_chanctx_ops(void) { - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) return; - ath9k_ops.hw_scan = ath9k_hw_scan; - ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; - ath9k_ops.remain_on_channel = ath9k_remain_on_channel; + ath9k_ops.hw_scan = ath9k_hw_scan; + ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; + ath9k_ops.remain_on_channel = ath9k_remain_on_channel; ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; - ath9k_ops.add_chanctx = ath9k_add_chanctx; - ath9k_ops.remove_chanctx = ath9k_remove_chanctx; - ath9k_ops.change_chanctx = ath9k_change_chanctx; - ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; - ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; - ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active; + ath9k_ops.add_chanctx = ath9k_add_chanctx; + ath9k_ops.remove_chanctx = ath9k_remove_chanctx; + ath9k_ops.change_chanctx = ath9k_change_chanctx; + ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; + ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; + ath9k_ops.mgd_prepare_tx = ath9k_mgd_prepare_tx; } +#endif + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 74ab1d02013b..2aaf233ee5d6 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -425,7 +425,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; - if (ath9k_use_chanctx && + if (ath9k_is_chanctx_enabled() && test_bit(ATH_OP_SCANNING, &common->op_flags)) rfilt |= ATH9K_RX_FILTER_BEACON; @@ -547,8 +547,8 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) "Reconfigure beacon timers based on synchronized timestamp\n"); if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0))) ath9k_set_beacon(sc); - if (sc->p2p_ps_vif) - ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); + + ath9k_p2p_beacon_sync(sc); } if (ath_beacon_dtim_pending_cab(skb)) { @@ -892,9 +892,10 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, return -EINVAL; } - if (rx_stats->is_mybeacon) { - sc->sched.next_tbtt = rx_stats->rs_tstamp; - ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED); + if (ath9k_is_chanctx_enabled()) { + if (rx_stats->is_mybeacon) + ath_chanctx_beacon_recv_ev(sc, rx_stats->rs_tstamp, + ATH_CHANCTX_EVENT_BEACON_RECEIVED); } ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status); diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/spectral.h index ead63412ee1a..7b410c6858b0 100644 --- a/drivers/net/wireless/ath/ath9k/spectral.h +++ b/drivers/net/wireless/ath/ath9k/spectral.h @@ -17,6 +17,8 @@ #ifndef SPECTRAL_H #define SPECTRAL_H +#include "../spectral_common.h" + /* enum spectral_mode: * * @SPECTRAL_DISABLED: spectral mode is disabled @@ -54,8 +56,6 @@ struct ath_ht20_mag_info { u8 max_exp; } __packed; -#define SPECTRAL_HT20_NUM_BINS 56 - /* WARNING: don't actually use this struct! MAC may vary the amount of * data by -1/+2. This struct is for reference only. */ @@ -83,8 +83,6 @@ struct ath_ht20_40_mag_info { u8 max_exp; } __packed; -#define SPECTRAL_HT20_40_NUM_BINS 128 - /* WARNING: don't actually use this struct! MAC may vary the amount of * data. This struct is for reference only. */ @@ -125,71 +123,6 @@ static inline u8 spectral_bitmap_weight(u8 *bins) return bins[0] & 0x3f; } -/* FFT sample format given to userspace via debugfs. - * - * Please keep the type/length at the front position and change - * other fields after adding another sample type - * - * TODO: this might need rework when switching to nl80211-based - * interface. - */ -enum ath_fft_sample_type { - ATH_FFT_SAMPLE_HT20 = 1, - ATH_FFT_SAMPLE_HT20_40, -}; - -struct fft_sample_tlv { - u8 type; /* see ath_fft_sample */ - __be16 length; - /* type dependent data follows */ -} __packed; - -struct fft_sample_ht20 { - struct fft_sample_tlv tlv; - - u8 max_exp; - - __be16 freq; - s8 rssi; - s8 noise; - - __be16 max_magnitude; - u8 max_index; - u8 bitmap_weight; - - __be64 tsf; - - u8 data[SPECTRAL_HT20_NUM_BINS]; -} __packed; - -struct fft_sample_ht20_40 { - struct fft_sample_tlv tlv; - - u8 channel_type; - __be16 freq; - - s8 lower_rssi; - s8 upper_rssi; - - __be64 tsf; - - s8 lower_noise; - s8 upper_noise; - - __be16 lower_max_magnitude; - __be16 upper_max_magnitude; - - u8 lower_max_index; - u8 upper_max_index; - - u8 lower_bitmap_weight; - u8 upper_bitmap_weight; - - u8 max_exp; - - u8 data[SPECTRAL_HT20_40_NUM_BINS]; -} __packed; - void ath9k_spectral_init_debug(struct ath_softc *sc); void ath9k_spectral_deinit_debug(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index a4f4f0da81f6..33531d9a4d50 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -193,7 +193,8 @@ int ath9k_suspend(struct ieee80211_hw *hw, u32 wow_triggers_enabled = 0; int ret = 0; - cancel_work_sync(&sc->chanctx_work); + ath9k_deinit_channel_context(sc); + mutex_lock(&sc->mutex); ath_cancel_work(sc); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 704fcbcbe20b..281986613fb2 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2632,8 +2632,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); - ath_chanctx_event(sc, NULL, - ATH_CHANCTX_EVENT_BEACON_SENT); + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_BEACON_SENT); + } + ath9k_csa_update(sc); continue; } diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index f8ded84b7be8..ef5b6dc7b7f1 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1430,18 +1430,10 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, if (!sta_info->ht_sta) return -EOPNOTSUPP; - rcu_read_lock(); - if (rcu_dereference(sta_info->agg[tid])) { - rcu_read_unlock(); - return -EBUSY; - } - tid_info = kzalloc(sizeof(struct carl9170_sta_tid), GFP_ATOMIC); - if (!tid_info) { - rcu_read_unlock(); + if (!tid_info) return -ENOMEM; - } tid_info->hsn = tid_info->bsn = tid_info->snx = (*ssn); tid_info->state = CARL9170_TID_STATE_PROGRESS; @@ -1460,7 +1452,6 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, list_add_tail_rcu(&tid_info->list, &ar->tx_ampdu_list); rcu_assign_pointer(sta_info->agg[tid], tid_info); spin_unlock_bh(&ar->tx_ampdu_list_lock); - rcu_read_unlock(); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 4cadfd48ffdf..ae86a600d920 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1557,7 +1557,7 @@ static struct carl9170_vif_info *carl9170_pick_beaconing_vif(struct ar9170 *ar) } out: - rcu_assign_pointer(ar->beacon_iter, cvif); + RCU_INIT_POINTER(ar->beacon_iter, cvif); return cvif; } diff --git a/drivers/net/wireless/ath/spectral_common.h b/drivers/net/wireless/ath/spectral_common.h new file mode 100644 index 000000000000..0d742acb1599 --- /dev/null +++ b/drivers/net/wireless/ath/spectral_common.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SPECTRAL_COMMON_H +#define SPECTRAL_COMMON_H + +#define SPECTRAL_HT20_NUM_BINS 56 +#define SPECTRAL_HT20_40_NUM_BINS 128 + +/* TODO: could possibly be 512, but no samples this large + * could be acquired so far. + */ +#define SPECTRAL_ATH10K_MAX_NUM_BINS 256 + +/* FFT sample format given to userspace via debugfs. + * + * Please keep the type/length at the front position and change + * other fields after adding another sample type + * + * TODO: this might need rework when switching to nl80211-based + * interface. + */ +enum ath_fft_sample_type { + ATH_FFT_SAMPLE_HT20 = 1, + ATH_FFT_SAMPLE_HT20_40, + ATH_FFT_SAMPLE_ATH10K, +}; + +struct fft_sample_tlv { + u8 type; /* see ath_fft_sample */ + __be16 length; + /* type dependent data follows */ +} __packed; + +struct fft_sample_ht20 { + struct fft_sample_tlv tlv; + + u8 max_exp; + + __be16 freq; + s8 rssi; + s8 noise; + + __be16 max_magnitude; + u8 max_index; + u8 bitmap_weight; + + __be64 tsf; + + u8 data[SPECTRAL_HT20_NUM_BINS]; +} __packed; + +struct fft_sample_ht20_40 { + struct fft_sample_tlv tlv; + + u8 channel_type; + __be16 freq; + + s8 lower_rssi; + s8 upper_rssi; + + __be64 tsf; + + s8 lower_noise; + s8 upper_noise; + + __be16 lower_max_magnitude; + __be16 upper_max_magnitude; + + u8 lower_max_index; + u8 upper_max_index; + + u8 lower_bitmap_weight; + u8 upper_bitmap_weight; + + u8 max_exp; + + u8 data[SPECTRAL_HT20_40_NUM_BINS]; +} __packed; + +struct fft_sample_ath10k { + struct fft_sample_tlv tlv; + u8 chan_width_mhz; + __be16 freq1; + __be16 freq2; + __be16 noise; + __be16 max_magnitude; + __be16 total_gain_db; + __be16 base_pwr_db; + __be64 tsf; + s8 max_index; + u8 rssi; + u8 relpwr_db; + u8 avgpwr_db; + u8 max_exp; + + u8 data[0]; +} __packed; + +#endif /* SPECTRAL_COMMON_H */ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4ac2c208c9ba..a00f31881df9 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -311,8 +311,10 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); - if (rc) + if (rc) { + del_timer_sync(&wil->scan_timer); wil->scan_request = NULL; + } return rc; } diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 8f66186adb8c..b1c6a7293390 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +22,7 @@ #include #include "wil6210.h" +#include "wmi.h" #include "txrx.h" /* Nasty hack. Better have per device instances */ @@ -29,6 +30,21 @@ static u32 mem_addr; static u32 dbg_txdesc_index; static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */ +enum dbg_off_type { + doff_u32 = 0, + doff_x32 = 1, + doff_ulong = 2, + doff_io32 = 3, +}; + +/* offset to "wil" */ +struct dbg_off { + const char *name; + umode_t mode; + ulong off; + enum dbg_off_type type; +}; + static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, const char *name, struct vring *vring, char _s, char _h) @@ -244,9 +260,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, static struct dentry *wil_debugfs_create_iomem_x32(const char *name, umode_t mode, struct dentry *parent, - void __iomem *value) + void *value) { - return debugfs_create_file(name, mode, parent, (void * __force)value, + return debugfs_create_file(name, mode, parent, value, &fops_iomem_x32); } @@ -270,6 +286,59 @@ static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode, return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong); } +/** + * wil6210_debugfs_init_offset - create set of debugfs files + * @wil - driver's context, used for printing + * @dbg - directory on the debugfs, where files will be created + * @base - base address used in address calculation + * @tbl - table with file descriptions. Should be terminated with empty element. + * + * Creates files accordingly to the @tbl. + */ +static void wil6210_debugfs_init_offset(struct wil6210_priv *wil, + struct dentry *dbg, void *base, + const struct dbg_off * const tbl) +{ + int i; + + for (i = 0; tbl[i].name; i++) { + struct dentry *f = NULL; + + switch (tbl[i].type) { + case doff_u32: + f = debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg, + base + tbl[i].off); + break; + case doff_x32: + f = debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg, + base + tbl[i].off); + break; + case doff_ulong: + f = wil_debugfs_create_ulong(tbl[i].name, tbl[i].mode, + dbg, base + tbl[i].off); + break; + case doff_io32: + f = wil_debugfs_create_iomem_x32(tbl[i].name, + tbl[i].mode, dbg, + base + tbl[i].off); + break; + } + if (IS_ERR_OR_NULL(f)) + wil_err(wil, "Create file \"%s\": err %ld\n", + tbl[i].name, PTR_ERR(f)); + } +} + +static const struct dbg_off isr_off[] = { + {"ICC", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, ICC), doff_io32}, + {"ICR", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, ICR), doff_io32}, + {"ICM", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, ICM), doff_io32}, + {"ICS", S_IWUSR, offsetof(struct RGF_ICR, ICS), doff_io32}, + {"IMV", S_IRUGO | S_IWUSR, offsetof(struct RGF_ICR, IMV), doff_io32}, + {"IMS", S_IWUSR, offsetof(struct RGF_ICR, IMS), doff_io32}, + {"IMC", S_IWUSR, offsetof(struct RGF_ICR, IMC), doff_io32}, + {}, +}; static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, const char *name, struct dentry *parent, u32 off) @@ -279,24 +348,19 @@ static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, if (IS_ERR_OR_NULL(d)) return -ENODEV; - wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, - wil->csr + off); - wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, - wil->csr + off + 4); - wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, - wil->csr + off + 8); - wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, - wil->csr + off + 12); - wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, - wil->csr + off + 16); - wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, - wil->csr + off + 20); - wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, - wil->csr + off + 24); + wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off, + isr_off); return 0; } +static const struct dbg_off pseudo_isr_off[] = { + {"CAUSE", S_IRUGO, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32}, + {"MASK_SW", S_IRUGO, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32}, + {"MASK_FW", S_IRUGO, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32}, + {}, +}; + static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, struct dentry *parent) { @@ -305,16 +369,19 @@ static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, if (IS_ERR_OR_NULL(d)) return -ENODEV; - wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + - HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); - wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + - HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); - wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + - HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, + pseudo_isr_off); return 0; } +static const struct dbg_off itr_cnt_off[] = { + {"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32}, + {"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32}, + {"CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32}, + {}, +}; + static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, struct dentry *parent) { @@ -323,12 +390,8 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, if (IS_ERR_OR_NULL(d)) return -ENODEV; - wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr + - HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); - wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr + - HOSTADDR(RGF_DMA_ITR_CNT_DATA)); - wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr + - HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, + itr_cnt_off); return 0; } @@ -666,16 +729,79 @@ static const struct file_operations fops_txdesc = { }; /*---------beamforming------------*/ +static char *wil_bfstatus_str(u32 status) +{ + switch (status) { + case 0: + return "Failed"; + case 1: + return "OK"; + case 2: + return "Retrying"; + default: + return "??"; + } +} + +static bool is_all_zeros(void * const x_, size_t sz) +{ + /* if reply is all-0, ignore this CID */ + u32 *x = x_; + int n; + + for (n = 0; n < sz / sizeof(*x); n++) + if (x[n]) + return false; + + return true; +} + static int wil_bf_debugfs_show(struct seq_file *s, void *data) { + int rc; + int i; struct wil6210_priv *wil = s->private; - seq_printf(s, - "TSF : 0x%016llx\n" - "TxMCS : %d\n" - "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", - wil->stats.tsf, wil->stats.bf_mcs, - wil->stats.my_rx_sector, wil->stats.my_tx_sector, - wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); + struct wmi_notify_req_cmd cmd = { + .interval_usec = 0, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_notify_req_done_event evt; + } __packed reply; + + for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { + u32 status; + + cmd.cid = i; + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + WMI_NOTIFY_REQ_DONE_EVENTID, &reply, + sizeof(reply), 20); + /* if reply is all-0, ignore this CID */ + if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt))) + continue; + + status = le32_to_cpu(reply.evt.status); + seq_printf(s, "CID %d {\n" + " TSF = 0x%016llx\n" + " TxMCS = %2d TxTpt = %4d\n" + " SQI = %4d\n" + " Status = 0x%08x %s\n" + " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n" + " Goodput(rx:tx) %4d:%4d\n" + "}\n", + i, + le64_to_cpu(reply.evt.tsf), + le16_to_cpu(reply.evt.bf_mcs), + le32_to_cpu(reply.evt.tx_tpt), + reply.evt.sqi, + status, wil_bfstatus_str(status), + le16_to_cpu(reply.evt.my_rx_sector), + le16_to_cpu(reply.evt.my_tx_sector), + le16_to_cpu(reply.evt.other_rx_sector), + le16_to_cpu(reply.evt.other_tx_sector), + le32_to_cpu(reply.evt.rx_goodput), + le32_to_cpu(reply.evt.tx_goodput)); + } return 0; } @@ -985,6 +1111,87 @@ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, } } +/* misc files */ +static const struct { + const char *name; + umode_t mode; + const struct file_operations *fops; +} dbg_files[] = { + {"mbox", S_IRUGO, &fops_mbox}, + {"vrings", S_IRUGO, &fops_vring}, + {"stations", S_IRUGO, &fops_sta}, + {"desc", S_IRUGO, &fops_txdesc}, + {"bf", S_IRUGO, &fops_bf}, + {"ssid", S_IRUGO | S_IWUSR, &fops_ssid}, + {"mem_val", S_IRUGO, &fops_memread}, + {"reset", S_IWUSR, &fops_reset}, + {"rxon", S_IWUSR, &fops_rxon}, + {"tx_mgmt", S_IWUSR, &fops_txmgmt}, + {"wmi_send", S_IWUSR, &fops_wmi}, + {"temp", S_IRUGO, &fops_temp}, + {"freq", S_IRUGO, &fops_freq}, + {"link", S_IRUGO, &fops_link}, + {"info", S_IRUGO, &fops_info}, +}; + +static void wil6210_debugfs_init_files(struct wil6210_priv *wil, + struct dentry *dbg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dbg_files); i++) + debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg, + wil, dbg_files[i].fops); +} + +/* interrupt control blocks */ +static const struct { + const char *name; + u32 icr_off; +} dbg_icr[] = { + {"USER_ICR", HOSTADDR(RGF_USER_USER_ICR)}, + {"DMA_EP_TX_ICR", HOSTADDR(RGF_DMA_EP_TX_ICR)}, + {"DMA_EP_RX_ICR", HOSTADDR(RGF_DMA_EP_RX_ICR)}, + {"DMA_EP_MISC_ICR", HOSTADDR(RGF_DMA_EP_MISC_ICR)}, +}; + +static void wil6210_debugfs_init_isr(struct wil6210_priv *wil, + struct dentry *dbg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dbg_icr); i++) + wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg, + dbg_icr[i].icr_off); +} + +#define WIL_FIELD(name, mode, type) { __stringify(name), mode, \ + offsetof(struct wil6210_priv, name), type} + +/* fields in struct wil6210_priv */ +static const struct dbg_off dbg_wil_off[] = { + WIL_FIELD(secure_pcp, S_IRUGO | S_IWUSR, doff_u32), + WIL_FIELD(status, S_IRUGO | S_IWUSR, doff_ulong), + WIL_FIELD(fw_version, S_IRUGO, doff_u32), + WIL_FIELD(hw_version, S_IRUGO, doff_x32), + {}, +}; + +static const struct dbg_off dbg_wil_regs[] = { + {"RGF_MAC_MTRL_COUNTER_0", S_IRUGO, HOSTADDR(RGF_MAC_MTRL_COUNTER_0), + doff_io32}, + {"RGF_USER_USAGE_1", S_IRUGO, HOSTADDR(RGF_USER_USAGE_1), doff_io32}, + {}, +}; + +/* static parameters */ +static const struct dbg_off dbg_statics[] = { + {"desc_index", S_IRUGO | S_IWUSR, (ulong)&dbg_txdesc_index, doff_u32}, + {"vring_index", S_IRUGO | S_IWUSR, (ulong)&dbg_vring_index, doff_u32}, + {"mem_addr", S_IRUGO | S_IWUSR, (ulong)&mem_addr, doff_u32}, + {}, +}; + int wil6210_debugfs_init(struct wil6210_priv *wil) { struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, @@ -993,51 +1200,17 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) if (IS_ERR_OR_NULL(dbg)) return -ENODEV; - debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); - debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); - debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta); - debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc); - debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg, - &dbg_txdesc_index); - debugfs_create_u32("vring_index", S_IRUGO | S_IWUSR, dbg, - &dbg_vring_index); - - debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); - debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); - debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, - &wil->secure_pcp); - wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg, - &wil->status); - debugfs_create_u32("fw_version", S_IRUGO, dbg, &wil->fw_version); - debugfs_create_x32("hw_version", S_IRUGO, dbg, &wil->hw_version); - - wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, - HOSTADDR(RGF_USER_USER_ICR)); - wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, - HOSTADDR(RGF_DMA_EP_TX_ICR)); - wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, - HOSTADDR(RGF_DMA_EP_RX_ICR)); - wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, - HOSTADDR(RGF_DMA_EP_MISC_ICR)); - wil6210_debugfs_create_pseudo_ISR(wil, dbg); - wil6210_debugfs_create_ITR_CNT(wil, dbg); - - wil_debugfs_create_iomem_x32("RGF_USER_USAGE_1", S_IRUGO, dbg, - wil->csr + - HOSTADDR(RGF_USER_USAGE_1)); - debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); - debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); - - debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); - debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon); - debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt); - debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi); - debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp); - debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq); - debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link); - debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info); - + wil6210_debugfs_init_files(wil, dbg); + wil6210_debugfs_init_isr(wil, dbg); wil6210_debugfs_init_blobs(wil, dbg); + wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off); + wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr, + dbg_wil_regs); + wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics); + + wil6210_debugfs_create_pseudo_ISR(wil, dbg); + + wil6210_debugfs_create_ITR_CNT(wil, dbg); return 0; } diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 67f1002a03a1..98bfbb6390b7 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 3704d2a434f3..b69d90f0716f 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,6 +25,9 @@ static bool no_fw_recovery; module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery"); +#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */ +#define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */ + /* * Due to a hardware issue, * one has to read/write to/from NIC in 32-bit chunks; @@ -309,7 +312,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) destroy_workqueue(wil->wmi_wq); } -static void wil_target_reset(struct wil6210_priv *wil) +static int wil_target_reset(struct wil6210_priv *wil) { int delay = 0; u32 hw_state; @@ -327,6 +330,8 @@ static void wil_target_reset(struct wil6210_priv *wil) /* register clear = read, AND with inverted, write */ #define C(a, v) W(a, R(a) & ~v) + wmb(); /* If host reorder writes here -> race in NIC */ + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ wil->hw_version = R(RGF_USER_FW_REV_ID); rev_id = wil->hw_version & 0xff; @@ -343,8 +348,9 @@ static void wil_target_reset(struct wil6210_priv *wil) wmb(); /* order is important here */ } - W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ + wmb(); /* If host reorder writes here -> race in NIC */ + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ wmb(); /* order is important here */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); @@ -385,14 +391,14 @@ static void wil_target_reset(struct wil6210_priv *wil) W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); wmb(); /* order is important here */ - /* wait until device ready */ + /* wait until device ready. typical time is 200..250 msec */ do { - msleep(1); + msleep(RST_DELAY); hw_state = R(RGF_USER_HW_MACHINE_STATE); - if (delay++ > 100) { + if (delay++ > RST_COUNT) { wil_err(wil, "Reset not completed, hw_state 0x%08x\n", hw_state); - return; + return -ETIME; } } while (hw_state != HW_MACHINE_BOOT_DONE); @@ -403,7 +409,8 @@ static void wil_target_reset(struct wil6210_priv *wil) C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); wmb(); /* order is important here */ - wil_dbg_misc(wil, "Reset completed in %d ms\n", delay); + wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY); + return 0; #undef R #undef W @@ -468,10 +475,11 @@ int wil_reset(struct wil6210_priv *wil) flush_workqueue(wil->wmi_wq_conn); flush_workqueue(wil->wmi_wq); - /* TODO: put MAC in reset */ - wil_target_reset(wil); - + rc = wil_target_reset(wil); wil_rx_fini(wil); + if (rc) + return rc; + /* init after reset */ wil->pending_connect_cid = -1; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 7afce6e8c507..a44c2b61be08 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -168,11 +168,15 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) void wil_if_free(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); + if (!ndev) return; - free_netdev(ndev); wil_priv_deinit(wil); + + wil_to_ndev(wil) = NULL; + free_netdev(ndev); + wil_wdev_free(wil); } diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index d3fbfa28db62..38dcbea49d44 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -218,12 +218,13 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) static void wil_pcie_remove(struct pci_dev *pdev) { struct wil6210_priv *wil = pci_get_drvdata(pdev); + void __iomem *csr = wil->csr; wil6210_debugfs_remove(wil); wil_if_pcie_disable(wil); wil_if_remove(wil); wil_if_free(wil); - pci_iounmap(pdev, wil->csr); + pci_iounmap(pdev, csr); pci_release_region(pdev, 0); pci_disable_device(pdev); } @@ -243,6 +244,8 @@ static const struct pci_device_id wil6210_pcie_ids[] = { .driver_data = (kernel_ulong_t)&wil_board_marlon }, { PCI_DEVICE(0x1ae9, 0x0310), .driver_data = (kernel_ulong_t)&wil_board_sparrow }, + { PCI_DEVICE(0x1ae9, 0x0302), /* same as above, firmware broken */ + .driver_data = (kernel_ulong_t)&wil_board_sparrow }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 180ca4793904..97c6a24716a1 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + #include "wil6210.h" #include "txrx.h" diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index d3467943d39d..9bd920d272bb 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -414,7 +414,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, cid = wil_rxdesc_cid(d); stats = &wil->sta[cid].stats; stats->last_mcs_rx = wil_rxdesc_mcs(d); - wil->stats.last_mcs_rx = stats->last_mcs_rx; /* use radiotap header only if required */ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index bc5706a4f007..a1ac4f87d47b 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 67e9624f7111..f8718fe547c4 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -133,6 +133,9 @@ struct RGF_ICR { #define RGF_HP_CTRL (0x88265c) #define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4) +/* MAC timer, usec, for packet lifetime */ +#define RGF_MAC_MTRL_COUNTER_0 (0x886aa8) + /* popular locations */ #define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ @@ -327,17 +330,6 @@ struct wil_tid_ampdu_rx { bool first_time; /* is it 1-st time this buffer used? */ }; -struct wil6210_stats { - u64 tsf; - u32 snr; - u16 last_mcs_rx; - u16 bf_mcs; /* last BF, used for Tx */ - u16 my_rx_sector; - u16 my_tx_sector; - u16 peer_rx_sector; - u16 peer_tx_sector; -}; - enum wil_sta_status { wil_sta_unused = 0, wil_sta_conn_pending = 1, @@ -430,7 +422,6 @@ struct wil6210_priv { struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ /* statistics */ - struct wil6210_stats stats; atomic_t isr_count_rx, isr_count_tx; /* debugfs */ struct dentry *debug; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 1d1d0afdd2e1..b1aaaee997d5 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include @@ -22,6 +23,10 @@ #include "wmi.h" #include "trace.h" +static uint max_assoc_sta = 1; +module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP"); + /** * WMI event receiving - theory of operations * @@ -346,11 +351,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) rx_mgmt_frame->bssid); cfg80211_put_bss(wiphy, bss); } else { - wil_err(wil, "cfg80211_inform_bss() failed\n"); + wil_err(wil, "cfg80211_inform_bss_frame() failed\n"); } } else { cfg80211_rx_mgmt(wil->wdev, freq, signal, - (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL); + (void *)rx_mgmt_frame, d_len, 0); } } @@ -482,33 +487,6 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, mutex_unlock(&wil->mutex); } -static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) -{ - struct wmi_notify_req_done_event *evt = d; - - if (len < sizeof(*evt)) { - wil_err(wil, "Short NOTIFY event\n"); - return; - } - - wil->stats.tsf = le64_to_cpu(evt->tsf); - wil->stats.snr = le32_to_cpu(evt->snr_val); - wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); - wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); - wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); - wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); - wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); - wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n" - "BF status 0x%08x SNR 0x%08x SQI %d%%\n" - "Tx Tpt %d goodput %d Rx goodput %d\n" - "Sectors(rx:tx) my %d:%d peer %d:%d\n", - wil->stats.bf_mcs, wil->stats.tsf, evt->status, - wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt), - le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), - wil->stats.my_rx_sector, wil->stats.my_tx_sector, - wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); -} - /* * Firmware reports EAPOL frame using WME event. * Reconstruct Ethernet frame and deliver it via normal Rx @@ -651,7 +629,6 @@ static const struct { {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, {WMI_CONNECT_EVENTID, wmi_evt_connect}, {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, - {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_linkup}, {WMI_WBE_LINKDOWN_EVENTID, wmi_evt_linkdown}, @@ -822,7 +799,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) .network_type = wmi_nettype, .disable_sec_offload = 1, .channel = chan - 1, - .pcp_max_assoc_sta = WIL6210_MAX_CID, + .pcp_max_assoc_sta = max_assoc_sta, }; struct { struct wil6210_mbox_hdr_wmi wmi; @@ -832,6 +809,14 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) if (!wil->secure_pcp) cmd.disable_sec = 1; + if ((cmd.pcp_max_assoc_sta > WIL6210_MAX_CID) || + (cmd.pcp_max_assoc_sta <= 0)) { + wil_info(wil, + "Requested connection limit %u, valid values are 1 - %d. Setting to %d\n", + max_assoc_sta, WIL6210_MAX_CID, WIL6210_MAX_CID); + cmd.pcp_max_assoc_sta = WIL6210_MAX_CID; + } + /* * Processing time may be huge, in case of secure AP it takes about * 3500ms for FW to start AP diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 17334c852866..061618cee9f6 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. * Copyright (c) 2006-2012 Wilocity . * * Permission to use, copy, modify, and/or distribute this software for any @@ -980,7 +980,7 @@ struct wmi_ready_event { * WMI_NOTIFY_REQ_DONE_EVENTID */ struct wmi_notify_req_done_event { - __le32 status; + __le32 status; /* beamforming status, 0: fail; 1: OK; 2: retrying */ __le64 tsf; __le32 snr_val; __le32 tx_tpt; diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index 4cfb4d99ced0..7afc9c5329fb 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -66,18 +66,18 @@ static void atmel_release(struct pcmcia_device *link); static void atmel_detach(struct pcmcia_device *p_dev); -typedef struct local_info_t { +struct local_info { struct net_device *eth_dev; -} local_info_t; +}; static int atmel_probe(struct pcmcia_device *p_dev) { - local_info_t *local; + struct local_info *local; dev_dbg(&p_dev->dev, "atmel_attach()\n"); /* Allocate space for private device-specific data */ - local = kzalloc(sizeof(local_info_t), GFP_KERNEL); + local = kzalloc(sizeof(*local), GFP_KERNEL); if (!local) return -ENOMEM; @@ -117,7 +117,7 @@ static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) static int atmel_config(struct pcmcia_device *link) { - local_info_t *dev; + struct local_info *dev; int ret; const struct pcmcia_device_id *did; @@ -141,14 +141,14 @@ static int atmel_config(struct pcmcia_device *link) if (ret) goto failed; - ((local_info_t*)link->priv)->eth_dev = + ((struct local_info *)link->priv)->eth_dev = init_atmel_card(link->irq, link->resource[0]->start, did ? did->driver_info : ATMEL_FW_TYPE_NONE, &link->dev, card_present, link); - if (!((local_info_t*)link->priv)->eth_dev) + if (!((struct local_info *)link->priv)->eth_dev) goto failed; @@ -161,20 +161,20 @@ static int atmel_config(struct pcmcia_device *link) static void atmel_release(struct pcmcia_device *link) { - struct net_device *dev = ((local_info_t*)link->priv)->eth_dev; + struct net_device *dev = ((struct local_info *)link->priv)->eth_dev; dev_dbg(&link->dev, "atmel_release\n"); if (dev) stop_atmel_card(dev); - ((local_info_t*)link->priv)->eth_dev = NULL; + ((struct local_info *)link->priv)->eth_dev = NULL; pcmcia_disable_device(link); } static int atmel_suspend(struct pcmcia_device *link) { - local_info_t *local = link->priv; + struct local_info *local = link->priv; netif_device_detach(local->eth_dev); @@ -183,7 +183,7 @@ static int atmel_suspend(struct pcmcia_device *link) static int atmel_resume(struct pcmcia_device *link) { - local_info_t *local = link->priv; + struct local_info *local = link->priv; atmel_open(local->eth_dev); netif_device_attach(local->eth_dev); diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index 6e00b8804ada..9f7965aae93d 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -18,6 +18,7 @@ b43-y += xmit.o b43-y += dma.o b43-y += pio.o b43-y += rfkill.o +b43-y += ppr.o b43-$(CONFIG_B43_LEDS) += leds.o b43-$(CONFIG_B43_PCMCIA) += pcmcia.o b43-$(CONFIG_B43_SDIO) += sdio.o diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 4113b6934764..95a943326420 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -457,6 +457,7 @@ enum { #define B43_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ #define B43_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ #define B43_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep frames with bad PLCP */ +#define B43_MACCTL_PHY_LOCK 0x00200000 #define B43_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ #define B43_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ #define B43_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ @@ -791,6 +792,13 @@ struct b43_firmware { bool pcm_request_failed; }; +enum b43_band { + B43_BAND_2G = 0, + B43_BAND_5G_LO = 1, + B43_BAND_5G_MI = 2, + B43_BAND_5G_HI = 3, +}; + /* Device (802.11 core) initialization status. */ enum { B43_STAT_UNINIT = 0, /* Uninitialized. */ @@ -1012,6 +1020,16 @@ static inline void b43_write16(struct b43_wldev *dev, u16 offset, u16 value) dev->dev->write16(dev->dev, offset, value); } +/* To optimize this check for flush_writes on BCM47XX_BCMA only. */ +static inline void b43_write16f(struct b43_wldev *dev, u16 offset, u16 value) +{ + b43_write16(dev, offset, value); +#if defined(CONFIG_BCM47XX_BCMA) + if (dev->dev->flush_writes) + b43_read16(dev, offset); +#endif +} + static inline void b43_maskset16(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) { diff --git a/drivers/net/wireless/b43/bus.c b/drivers/net/wireless/b43/bus.c index 565fdbdd6915..17d16a391fe6 100644 --- a/drivers/net/wireless/b43/bus.c +++ b/drivers/net/wireless/b43/bus.c @@ -22,6 +22,10 @@ */ +#ifdef CONFIG_BCM47XX_BCMA +#include +#endif + #include "b43.h" #include "bus.h" @@ -102,6 +106,12 @@ struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core) dev->write32 = b43_bus_bcma_write32; dev->block_read = b43_bus_bcma_block_read; dev->block_write = b43_bus_bcma_block_write; +#ifdef CONFIG_BCM47XX_BCMA + if (b43_bus_host_is_pci(dev) && + bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA && + bcm47xx_bus.bcma.bus.chipinfo.id == BCMA_CHIP_ID_BCM4716) + dev->flush_writes = true; +#endif dev->dev = &core->dev; dev->dma_dev = core->dma_dev; diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/b43/bus.h index f3205c6988bc..256c2c17939a 100644 --- a/drivers/net/wireless/b43/bus.h +++ b/drivers/net/wireless/b43/bus.h @@ -33,6 +33,7 @@ struct b43_bus_dev { size_t count, u16 offset, u8 reg_width); void (*block_write)(struct b43_bus_dev *dev, const void *buffer, size_t count, u16 offset, u8 reg_width); + bool flush_writes; struct device *dev; struct device *dma_dev; @@ -60,7 +61,21 @@ static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev) #else return false; #endif +}; + +static inline bool b43_bus_host_is_pci(struct b43_bus_dev *dev) +{ +#ifdef CONFIG_B43_BCMA + if (dev->bus_type == B43_BUS_BCMA) + return (dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI); +#endif +#ifdef CONFIG_B43_SSB + if (dev->bus_type == B43_BUS_SSB) + return (dev->sdev->bus->bustype == SSB_BUSTYPE_PCI); +#endif + return false; } + static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev) { #ifdef CONFIG_B43_SSB diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 2af1ac396eb4..66ff718cc412 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4466,10 +4466,10 @@ static int b43_phy_versioning(struct b43_wldev *dev) if (core_rev == 40 || core_rev == 42) { radio_manuf = 0x17F; - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, 0); radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA); - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, 1); radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA); radio_ver = 0; /* Is there version somewhere? */ @@ -4477,7 +4477,7 @@ static int b43_phy_versioning(struct b43_wldev *dev) u16 radio24[3]; for (tmp = 0; tmp < 3; tmp++) { - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, tmp); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, tmp); radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA); } @@ -4494,13 +4494,12 @@ static int b43_phy_versioning(struct b43_wldev *dev) else tmp = 0x5205017F; } else { - b43_write16(dev, B43_MMIO_RADIO_CONTROL, - B43_RADIOCTL_ID); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, + B43_RADIOCTL_ID); tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); - b43_write16(dev, B43_MMIO_RADIO_CONTROL, - B43_RADIOCTL_ID); - tmp |= (u32)b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) - << 16; + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, + B43_RADIOCTL_ID); + tmp |= b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) << 16; } radio_manuf = (tmp & 0x00000FFF); radio_id = (tmp & 0x0FFFF000) >> 12; diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c index 25e40432d68b..99c036f5ecb7 100644 --- a/drivers/net/wireless/b43/phy_a.c +++ b/drivers/net/wireless/b43/phy_a.c @@ -444,14 +444,14 @@ static inline u16 adjust_phyreg(struct b43_wldev *dev, u16 offset) static u16 b43_aphy_op_read(struct b43_wldev *dev, u16 reg) { reg = adjust_phyreg(dev, reg); - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_aphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) { reg = adjust_phyreg(dev, reg); - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 3cbef21b4726..1dfc682a8055 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -222,12 +222,18 @@ static inline void assert_mac_suspended(struct b43_wldev *dev) u16 b43_radio_read(struct b43_wldev *dev, u16 reg) { assert_mac_suspended(dev); + dev->phy.writes_counter = 0; return dev->phy.ops->radio_read(dev, reg); } void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { assert_mac_suspended(dev); + if (b43_bus_host_is_pci(dev->dev) && + ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { + b43_read32(dev, B43_MMIO_MACCTL); + dev->phy.writes_counter = 1; + } dev->phy.ops->radio_write(dev, reg, value); } @@ -268,17 +274,28 @@ u16 b43_phy_read(struct b43_wldev *dev, u16 reg) { assert_mac_suspended(dev); dev->phy.writes_counter = 0; - return dev->phy.ops->phy_read(dev, reg); + + if (dev->phy.ops->phy_read) + return dev->phy.ops->phy_read(dev, reg); + + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + return b43_read16(dev, B43_MMIO_PHY_DATA); } void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) { assert_mac_suspended(dev); - dev->phy.ops->phy_write(dev, reg, value); - if (++dev->phy.writes_counter == B43_MAX_WRITES_IN_ROW) { + if (b43_bus_host_is_pci(dev->dev) && + ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { b43_read16(dev, B43_MMIO_PHY_VER); - dev->phy.writes_counter = 0; + dev->phy.writes_counter = 1; } + + if (dev->phy.ops->phy_write) + return dev->phy.ops->phy_write(dev, reg, value); + + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, value); } void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c index 8f5c14bc10e6..727ce6edb4b3 100644 --- a/drivers/net/wireless/b43/phy_g.c +++ b/drivers/net/wireless/b43/phy_g.c @@ -2555,13 +2555,13 @@ static void b43_gphy_op_exit(struct b43_wldev *dev) static u16 b43_gphy_op_read(struct b43_wldev *dev, u16 reg) { - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); return b43_read16(dev, B43_MMIO_PHY_DATA); } static void b43_gphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) { - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, value); } @@ -2572,7 +2572,7 @@ static u16 b43_gphy_op_radio_read(struct b43_wldev *dev, u16 reg) /* G-PHY needs 0x80 for read access. */ reg |= 0x80; - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } @@ -2581,7 +2581,7 @@ static void b43_gphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/b43/phy_ht.c index f2974c6b1c01..c4dc8b020f60 100644 --- a/drivers/net/wireless/b43/phy_ht.c +++ b/drivers/net/wireless/b43/phy_ht.c @@ -1071,22 +1071,10 @@ static unsigned int b43_phy_ht_op_get_default_chan(struct b43_wldev *dev) * R/W ops. **************************************************/ -static u16 b43_phy_ht_op_read(struct b43_wldev *dev, u16 reg) -{ - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -static void b43_phy_ht_op_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - static void b43_phy_ht_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); } @@ -1096,14 +1084,14 @@ static u16 b43_phy_ht_op_radio_read(struct b43_wldev *dev, u16 reg) /* HT-PHY needs 0x200 for read access */ reg |= 0x200; - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO24_DATA); } static void b43_phy_ht_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO24_DATA, value); } @@ -1126,8 +1114,6 @@ const struct b43_phy_operations b43_phyops_ht = { .free = b43_phy_ht_op_free, .prepare_structs = b43_phy_ht_op_prepare_structs, .init = b43_phy_ht_op_init, - .phy_read = b43_phy_ht_op_read, - .phy_write = b43_phy_ht_op_write, .phy_maskset = b43_phy_ht_op_maskset, .radio_read = b43_phy_ht_op_radio_read, .radio_write = b43_phy_ht_op_radio_write, diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/b43/phy_lcn.c index e76bbdf3247e..97461ccf3e1e 100644 --- a/drivers/net/wireless/b43/phy_lcn.c +++ b/drivers/net/wireless/b43/phy_lcn.c @@ -810,22 +810,10 @@ static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev) * R/W ops. **************************************************/ -static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg) -{ - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); } @@ -835,14 +823,14 @@ static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg) /* LCN-PHY needs 0x200 for read access */ reg |= 0x200; - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO24_DATA); } static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { - b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO24_DATA, value); } @@ -855,8 +843,6 @@ const struct b43_phy_operations b43_phyops_lcn = { .free = b43_phy_lcn_op_free, .prepare_structs = b43_phy_lcn_op_prepare_structs, .init = b43_phy_lcn_op_init, - .phy_read = b43_phy_lcn_op_read, - .phy_write = b43_phy_lcn_op_write, .phy_maskset = b43_phy_lcn_op_maskset, .radio_read = b43_phy_lcn_op_radio_read, .radio_write = b43_phy_lcn_op_radio_write, diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 92190dacf689..058a9f232050 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -1985,22 +1985,10 @@ static void lpphy_calibration(struct b43_wldev *dev) b43_mac_enable(dev); } -static u16 b43_lpphy_op_read(struct b43_wldev *dev, u16 reg) -{ - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -static void b43_lpphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); b43_write16(dev, B43_MMIO_PHY_DATA, (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); } @@ -2016,7 +2004,7 @@ static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg) } else reg |= 0x200; - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } @@ -2025,7 +2013,7 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } @@ -2713,8 +2701,6 @@ const struct b43_phy_operations b43_phyops_lp = { .free = b43_lpphy_op_free, .prepare_structs = b43_lpphy_op_prepare_structs, .init = b43_lpphy_op_init, - .phy_read = b43_lpphy_op_read, - .phy_write = b43_lpphy_op_write, .phy_maskset = b43_lpphy_op_maskset, .radio_read = b43_lpphy_op_radio_read, .radio_write = b43_lpphy_op_radio_write, diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index e2a3f0d5bcc2..cf625d8732a7 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -34,6 +34,7 @@ #include "radio_2056.h" #include "radio_2057.h" #include "main.h" +#include "ppr.h" struct nphy_txgains { u16 tx_lpf[2]; @@ -3606,16 +3607,6 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, * Tx and Rx **************************************************/ -static void b43_nphy_op_adjust_txpower(struct b43_wldev *dev) -{//TODO -} - -static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev, - bool ignore_tssi) -{//TODO - return B43_TXPWR_RES_DONE; -} - /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) { @@ -4069,6 +4060,7 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) s16 a1[2], b0[2], b1[2]; u8 idle[2]; + u8 ppr_max; s8 target[2]; s32 num, den, pwr; u32 regval[64]; @@ -4147,7 +4139,12 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) b1[0] = b1[1] = -1393; } } - /* target[0] = target[1] = nphy->tx_power_max; */ + + ppr_max = b43_ppr_get_max(dev, &nphy->tx_pwr_max_ppr); + if (ppr_max) { + target[0] = ppr_max; + target[1] = ppr_max; + } if (dev->phy.rev >= 3) { if (sprom->fem.ghz2.tssipos) @@ -4235,8 +4232,9 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) const u32 *table = NULL; u32 rfpwr_offset; - u8 pga_gain; + u8 pga_gain, pad_gain; int i; + const s16 *uninitialized_var(rf_pwr_offset_table); table = b43_nphy_get_tx_gain_table(dev); if (!table) @@ -4252,13 +4250,27 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) nphy->gmval = (table[0] >> 16) & 0x7000; #endif + if (phy->rev >= 19) { + return; + } else if (phy->rev >= 7) { + rf_pwr_offset_table = b43_ntab_get_rf_pwr_offset_table(dev); + if (!rf_pwr_offset_table) + return; + /* TODO: Enable this once we have gains configured */ + return; + } + for (i = 0; i < 128; i++) { if (phy->rev >= 19) { /* TODO */ return; } else if (phy->rev >= 7) { - /* TODO */ - return; + pga_gain = (table[i] >> 24) & 0xf; + pad_gain = (table[i] >> 19) & 0x1f; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + rfpwr_offset = rf_pwr_offset_table[pad_gain]; + else + rfpwr_offset = rf_pwr_offset_table[pga_gain]; } else { pga_gain = (table[i] >> 24) & 0xF; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) @@ -5874,6 +5886,69 @@ static void b43_nphy_set_rx_core_state(struct b43_wldev *dev, u8 mask) b43_mac_enable(dev); } +static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev, + bool ignore_tssi) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; + struct b43_ppr *ppr = &nphy->tx_pwr_max_ppr; + u8 max; /* qdBm */ + bool tx_pwr_state; + + if (nphy->tx_pwr_last_recalc_freq == channel->center_freq && + nphy->tx_pwr_last_recalc_limit == phy->desired_txpower) + return B43_TXPWR_RES_DONE; + + /* Make sure we have a clean PPR */ + b43_ppr_clear(dev, ppr); + + /* HW limitations */ + b43_ppr_load_max_from_sprom(dev, ppr, B43_BAND_2G); + + /* Regulatory & user settings */ + max = INT_TO_Q52(phy->chandef->chan->max_power); + if (phy->desired_txpower) + max = min_t(u8, max, INT_TO_Q52(phy->desired_txpower)); + b43_ppr_apply_max(dev, ppr, max); + if (b43_debug(dev, B43_DBG_XMITPOWER)) + b43dbg(dev->wl, "Calculated TX power: " Q52_FMT "\n", + Q52_ARG(b43_ppr_get_max(dev, ppr))); + + /* TODO: Enable this once we get gains working */ +#if 0 + /* Some extra gains */ + hw_gain = 6; /* N-PHY specific */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + hw_gain += sprom->antenna_gain.a0; + else + hw_gain += sprom->antenna_gain.a1; + b43_ppr_add(dev, ppr, -hw_gain); +#endif + + /* Make sure we didn't go too low */ + b43_ppr_apply_min(dev, ppr, INT_TO_Q52(8)); + + /* Apply */ + tx_pwr_state = nphy->txpwrctrl; + b43_mac_suspend(dev); + b43_nphy_tx_power_ctl_setup(dev); + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_PHY_LOCK); + b43_read32(dev, B43_MMIO_MACCTL); + udelay(1); + } + b43_nphy_tx_power_ctrl(dev, nphy->txpwrctrl); + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PHY_LOCK, 0); + b43_mac_enable(dev); + + nphy->tx_pwr_last_recalc_freq = channel->center_freq; + nphy->tx_pwr_last_recalc_limit = phy->desired_txpower; + + return B43_TXPWR_RES_DONE; +} + /************************************************** * N-PHY init **************************************************/ @@ -6407,6 +6482,7 @@ static int b43_nphy_op_allocate(struct b43_wldev *dev) nphy = kzalloc(sizeof(*nphy), GFP_KERNEL); if (!nphy) return -ENOMEM; + dev->phy.n = nphy; return 0; @@ -6497,26 +6573,13 @@ static inline void check_phyreg(struct b43_wldev *dev, u16 offset) #endif /* B43_DEBUG */ } -static u16 b43_nphy_op_read(struct b43_wldev *dev, u16 reg) -{ - check_phyreg(dev, reg); - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -static void b43_nphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - check_phyreg(dev, reg); - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, u16 set) { check_phyreg(dev, reg); - b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); b43_maskset16(dev, B43_MMIO_PHY_DATA, mask, set); + dev->phy.writes_counter = 1; } static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) @@ -6529,7 +6592,7 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) else reg |= 0x100; - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); } @@ -6538,7 +6601,7 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) /* Register 1 is a 32-bit register. */ B43_WARN_ON(dev->phy.rev < 7 && reg == 1); - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); } @@ -6652,8 +6715,6 @@ const struct b43_phy_operations b43_phyops_n = { .free = b43_nphy_op_free, .prepare_structs = b43_nphy_op_prepare_structs, .init = b43_nphy_op_init, - .phy_read = b43_nphy_op_read, - .phy_write = b43_nphy_op_write, .phy_maskset = b43_nphy_op_maskset, .radio_read = b43_nphy_op_radio_read, .radio_write = b43_nphy_op_radio_write, @@ -6662,5 +6723,4 @@ const struct b43_phy_operations b43_phyops_n = { .switch_channel = b43_nphy_op_switch_channel, .get_default_chan = b43_nphy_op_get_default_chan, .recalc_txpower = b43_nphy_op_recalc_txpower, - .adjust_txpower = b43_nphy_op_adjust_txpower, }; diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 30bec815b969..a6da2c31a99c 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -2,6 +2,7 @@ #define B43_NPHY_H_ #include "phy_common.h" +#include "ppr.h" /* N-PHY registers. */ @@ -967,6 +968,9 @@ struct b43_phy_n { struct b43_phy_n_txpwrindex txpwrindex[2]; struct b43_phy_n_pwr_ctl_info pwr_ctl_info[2]; struct b43_chanspec txiqlocal_chanspec; + struct b43_ppr tx_pwr_max_ppr; + u16 tx_pwr_last_recalc_freq; + int tx_pwr_last_recalc_limit; u8 txrx_chain; u16 tx_rx_cal_phy_saveregs[11]; diff --git a/drivers/net/wireless/b43/ppr.c b/drivers/net/wireless/b43/ppr.c new file mode 100644 index 000000000000..9a770279c415 --- /dev/null +++ b/drivers/net/wireless/b43/ppr.c @@ -0,0 +1,199 @@ +/* + * Broadcom B43 wireless driver + * PPR (Power Per Rate) management + * + * Copyright (c) 2014 RafaÅ‚ MiÅ‚ecki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ppr.h" +#include "b43.h" + +#define ppr_for_each_entry(ppr, i, entry) \ + for (i = 0, entry = &(ppr)->__all_rates[i]; \ + i < B43_PPR_RATES_NUM; \ + i++, entry++) + +void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr) +{ + memset(ppr, 0, sizeof(*ppr)); + + /* Compile-time PPR check */ + BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM * sizeof(u8)); +} + +void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff) +{ + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + *rate = clamp_val(*rate + diff, 0, 127); + } +} + +void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max) +{ + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + *rate = min(*rate, max); + } +} + +void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min) +{ + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + *rate = max(*rate, min); + } +} + +u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr) +{ + u8 res = 0; + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + res = max(*rate, res); + } + + return res; +} + +bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, + enum b43_band band) +{ + struct b43_ppr_rates *rates = &ppr->rates; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + u8 maxpwr, off; + u32 sprom_ofdm_po; + u16 *sprom_mcs_po; + u8 extra_cdd_po, extra_stbc_po; + int i; + + switch (band) { + case B43_BAND_2G: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_2g, + sprom->core_pwr_info[1].maxpwr_2g); + sprom_ofdm_po = sprom->ofdm2gpo; + sprom_mcs_po = sprom->mcs2gpo; + extra_cdd_po = (sprom->cddpo >> 0) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 0) & 0xf; + break; + case B43_BAND_5G_LO: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gl, + sprom->core_pwr_info[1].maxpwr_5gl); + sprom_ofdm_po = sprom->ofdm5glpo; + sprom_mcs_po = sprom->mcs5glpo; + extra_cdd_po = (sprom->cddpo >> 8) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 8) & 0xf; + break; + case B43_BAND_5G_MI: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_5g, + sprom->core_pwr_info[1].maxpwr_5g); + sprom_ofdm_po = sprom->ofdm5gpo; + sprom_mcs_po = sprom->mcs5gpo; + extra_cdd_po = (sprom->cddpo >> 4) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 4) & 0xf; + break; + case B43_BAND_5G_HI: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gh, + sprom->core_pwr_info[1].maxpwr_5gh); + sprom_ofdm_po = sprom->ofdm5ghpo; + sprom_mcs_po = sprom->mcs5ghpo; + extra_cdd_po = (sprom->cddpo >> 12) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 12) & 0xf; + break; + default: + WARN_ON_ONCE(1); + return false; + } + + if (band == B43_BAND_2G) { + for (i = 0; i < 4; i++) { + off = ((sprom->cck2gpo >> (i * 4)) & 0xf) * 2; + rates->cck[i] = maxpwr - off; + } + } + + /* OFDM */ + for (i = 0; i < 8; i++) { + off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2; + rates->ofdm[i] = maxpwr - off; + } + + /* MCS 20 SISO */ + rates->mcs_20[0] = rates->ofdm[0]; + rates->mcs_20[1] = rates->ofdm[2]; + rates->mcs_20[2] = rates->ofdm[3]; + rates->mcs_20[3] = rates->ofdm[4]; + rates->mcs_20[4] = rates->ofdm[5]; + rates->mcs_20[5] = rates->ofdm[6]; + rates->mcs_20[6] = rates->ofdm[7]; + rates->mcs_20[7] = rates->ofdm[7]; + + /* MCS 20 CDD */ + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_cdd[i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_cdd[i] -= extra_cdd_po; + } + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_cdd[4 + i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_cdd[4 + i] -= extra_cdd_po; + } + + /* OFDM 20 CDD */ + rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0]; + rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0]; + rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1]; + rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2]; + rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3]; + rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4]; + rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5]; + rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6]; + + /* MCS 20 STBC */ + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_stbc[i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_stbc[i] -= extra_stbc_po; + } + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_stbc[4 + i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_stbc[4 + i] -= extra_stbc_po; + } + + /* MCS 20 SDM */ + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_sdm[i] = maxpwr - off; + } + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_sdm[4 + i] = maxpwr - off; + } + + return true; +} diff --git a/drivers/net/wireless/b43/ppr.h b/drivers/net/wireless/b43/ppr.h new file mode 100644 index 000000000000..24d7447e9f01 --- /dev/null +++ b/drivers/net/wireless/b43/ppr.h @@ -0,0 +1,45 @@ +#ifndef LINUX_B43_PPR_H_ +#define LINUX_B43_PPR_H_ + +#include + +#define B43_PPR_CCK_RATES_NUM 4 +#define B43_PPR_OFDM_RATES_NUM 8 +#define B43_PPR_MCS_RATES_NUM 8 + +#define B43_PPR_RATES_NUM (B43_PPR_CCK_RATES_NUM + \ + B43_PPR_OFDM_RATES_NUM * 2 + \ + B43_PPR_MCS_RATES_NUM * 4) + +struct b43_ppr_rates { + u8 cck[B43_PPR_CCK_RATES_NUM]; + u8 ofdm[B43_PPR_OFDM_RATES_NUM]; + u8 ofdm_20_cdd[B43_PPR_OFDM_RATES_NUM]; + u8 mcs_20[B43_PPR_MCS_RATES_NUM]; /* SISO */ + u8 mcs_20_cdd[B43_PPR_MCS_RATES_NUM]; + u8 mcs_20_stbc[B43_PPR_MCS_RATES_NUM]; + u8 mcs_20_sdm[B43_PPR_MCS_RATES_NUM]; +}; + +struct b43_ppr { + /* All powers are in qdbm (Q5.2) */ + union { + u8 __all_rates[B43_PPR_RATES_NUM]; + struct b43_ppr_rates rates; + }; +}; + +struct b43_wldev; +enum b43_band; + +void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr); + +void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff); +void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max); +void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min); +u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr); + +bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, + enum b43_band band); + +#endif /* LINUX_B43_PPR_H_ */ diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 4b5885077b01..25d1cbd34306 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2878,6 +2878,40 @@ const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = { -54, -46, -39, -31, -23, -15, -8, 0 }; +/* Extracted from MMIO dump of 6.30.223.248 + * Entries: 0, 15, 17, 21, 24, 26, 27, 29, 30 were guessed + */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev9_2g[] = { + -133, -133, -107, -92, -81, + -73, -66, -61, -56, -52, + -48, -44, -41, -37, -34, + -31, -28, -25, -22, -19, + -17, -14, -12, -10, -9, + -7, -5, -4, -3, -2, + -1, 0, +}; + +/* Extracted from MMIO dump of 6.30.223.248 */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev9_5g[] = { + -101, -94, -86, -79, -72, + -65, -57, -50, -42, -35, + -28, -21, -16, -9, -4, + 0, +}; + +/* Extracted from MMIO dump of 6.30.223.248 + * Entries: 0, 26, 28, 29, 30, 31 were guessed + */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev14_2g[] = { + -111, -111, -111, -84, -70, + -59, -52, -45, -40, -36, + -32, -29, -26, -23, -21, + -18, -16, -15, -13, -11, + -10, -8, -7, -6, -5, + -4, -4, -3, -3, -2, + -2, -1, +}; + const u16 tbl_iqcal_gainparams[2][9][8] = { { { 0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69 }, @@ -3197,7 +3231,7 @@ static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_workaround[2][4] = { { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, 0x527E, /* invalid for external LNA! */ { 0x513F, 0x513F, 0x513F, 0x513F }, /* invalid for external LNA! */ - 0x1076, 0x0066, 0x0000, /* low is invalid (the last one) */ + 0x007E, 0x0066, 0x0000, /* low is invalid (the last one) */ 0x18, 0x18, 0x18, 0x01D0, 0x5, }, @@ -3708,9 +3742,43 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) } } +const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + switch (phy->rev) { + case 17: + if (phy->radio_rev == 14) + return b43_ntab_rf_pwr_offset_2057_rev14_2g; + break; + case 16: + if (phy->radio_rev == 9) + return b43_ntab_rf_pwr_offset_2057_rev9_2g; + break; + } + + b43err(dev->wl, + "No 2GHz RF power table available for this device\n"); + return NULL; + } else { + switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_rf_pwr_offset_2057_rev9_5g; + break; + } + + b43err(dev->wl, + "No 5GHz RF power table available for this device\n"); + return NULL; + } +} + struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( struct b43_wldev *dev, bool ghz5, bool ext_lna) { + struct b43_phy *phy = &dev->phy; struct nphy_gain_ctl_workaround_entry *e; u8 phy_idx; @@ -3729,37 +3797,49 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( e = &nphy_gain_ctl_workaround[ghz5][phy_idx]; /* Some workarounds to the workarounds... */ - if (ghz5 && dev->phy.rev >= 6) { - if (dev->phy.radio_rev == 11 && - !b43_is_40mhz(dev)) - e->cliplo_gain = 0x2d; - } else if (!ghz5 && dev->phy.rev >= 5) { - static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a, - 0x106c, 0x1074, 0x107c, 0x207c}; + if (!ghz5) { u8 tr_iso = dev->dev->bus_sprom->fem.ghz2.tr_iso; - if (ext_lna) { + if (tr_iso > 7) + tr_iso = 3; + + if (phy->rev >= 6) { + static const int gain_data[] = { 0x106a, 0x106c, 0x1074, + 0x107c, 0x007e, 0x107e, + 0x207e, 0x307e, }; + + e->cliplo_gain = gain_data[tr_iso]; + } else if (phy->rev == 5) { + static const int gain_data[] = { 0x0062, 0x0064, 0x006a, + 0x106a, 0x106c, 0x1074, + 0x107c, 0x207c, }; + + e->cliplo_gain = gain_data[tr_iso]; + } + + if (phy->rev >= 5 && ext_lna) { e->rfseq_init[0] &= ~0x4000; e->rfseq_init[1] &= ~0x4000; e->rfseq_init[2] &= ~0x4000; e->rfseq_init[3] &= ~0x4000; e->init_gain &= ~0x4000; } - if (tr_iso > 7) - tr_iso = 3; - e->cliplo_gain = gain_data[tr_iso]; - - } else if (ghz5 && dev->phy.rev == 4 && ext_lna) { - e->rfseq_init[0] &= ~0x4000; - e->rfseq_init[1] &= ~0x4000; - e->rfseq_init[2] &= ~0x4000; - e->rfseq_init[3] &= ~0x4000; - e->init_gain &= ~0x4000; - e->rfseq_init[0] |= 0x1000; - e->rfseq_init[1] |= 0x1000; - e->rfseq_init[2] |= 0x1000; - e->rfseq_init[3] |= 0x1000; - e->init_gain |= 0x1000; + } else { + if (phy->rev >= 6) { + if (phy->radio_rev == 11 && !b43_is_40mhz(dev)) + e->crsminu = 0x2d; + } else if (phy->rev == 4 && ext_lna) { + e->rfseq_init[0] &= ~0x4000; + e->rfseq_init[1] &= ~0x4000; + e->rfseq_init[2] &= ~0x4000; + e->rfseq_init[3] &= ~0x4000; + e->init_gain &= ~0x4000; + e->rfseq_init[0] |= 0x1000; + e->rfseq_init[1] |= 0x1000; + e->rfseq_init[2] |= 0x1000; + e->rfseq_init[3] |= 0x1000; + e->init_gain |= 0x1000; + } } return e; diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 3ce2e6f3a278..b51f386db02f 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -191,6 +191,8 @@ void b43_nphy_tables_init(struct b43_wldev *dev); const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev); +const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev); + extern const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[]; extern const u16 tbl_iqcal_gainparams[2][9][8]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 057b982ea8b3..1d78a91db594 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -1431,8 +1431,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, IEEE80211_BAND_5GHZ); wdev = &ifp->vif->wdev; - cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0, - GFP_ATOMIC); + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0); kfree(mgmt_frame); return 0; @@ -1896,8 +1895,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0, - GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", mgmt_frame_len, e->datalen, chanspec, freq); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 02fe706fc9ec..12a60ca1462a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2394,9 +2394,13 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); brcmf_dbg(CONN, "Signal: %d\n", notify_signal); - bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID, - 0, notify_capability, notify_interval, notify_ie, - notify_ielen, notify_signal, GFP_KERNEL); + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, + (const u8 *)bi->BSSID, + 0, notify_capability, + notify_interval, notify_ie, + notify_ielen, notify_signal, + GFP_KERNEL); if (!bss) return -ENOMEM; @@ -2498,9 +2502,11 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); brcmf_dbg(CONN, "signal: %d\n", notify_signal); - bss = cfg80211_inform_bss(wiphy, notify_channel, bssid, - 0, notify_capability, notify_interval, - notify_ie, notify_ielen, notify_signal, GFP_KERNEL); + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, + GFP_KERNEL); if (!bss) { err = -ENOMEM; diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index 40078f5f932e..964b64ab7fe3 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -398,7 +398,7 @@ static int cw1200_spi_probe(struct spi_device *func) return -1; } - self = kzalloc(sizeof(*self), GFP_KERNEL); + self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL); if (!self) { pr_err("Can't allocate SPI hwbus_priv."); return -ENOMEM; @@ -424,7 +424,6 @@ static int cw1200_spi_probe(struct spi_device *func) if (status) { cw1200_spi_irq_unsubscribe(self); cw1200_spi_off(plat_data); - kfree(self); } return status; @@ -441,7 +440,6 @@ static int cw1200_spi_disconnect(struct spi_device *func) cw1200_core_release(self->core); self->core = NULL; } - kfree(self); } cw1200_spi_off(dev_get_platdata(&func->dev)); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index a42f9c335090..f0c3c77a48d3 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -5552,7 +5552,7 @@ static int ipw_find_adhoc_network(struct ipw_priv *priv, min(network->ssid_len, priv->essid_len)))) { char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; - strncpy(escaped, + strlcpy(escaped, print_ssid(ssid, network->ssid, network->ssid_len), sizeof(escaped)); @@ -5765,7 +5765,7 @@ static int ipw_best_network(struct ipw_priv *priv, memcmp(network->ssid, priv->essid, min(network->ssid_len, priv->essid_len)))) { char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; - strncpy(escaped, + strlcpy(escaped, print_ssid(ssid, network->ssid, network->ssid_len), sizeof(escaped)); @@ -5782,7 +5782,7 @@ static int ipw_best_network(struct ipw_priv *priv, * testing everything else. */ if (match->network && match->network->stats.rssi > network->stats.rssi) { char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; - strncpy(escaped, + strlcpy(escaped, print_ssid(ssid, network->ssid, network->ssid_len), sizeof(escaped)); IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded because " diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 824f5e287783..267e48a2915e 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -85,6 +85,16 @@ config IWLWIFI_BCAST_FILTERING If unsure, don't enable this option, as some programs might expect incoming broadcasts for their normal operations. +config IWLWIFI_UAPSD + bool "enable U-APSD by default" + depends on IWLMVM + help + Say Y here to enable U-APSD by default. This may cause + interoperability problems with some APs, manifesting in lower than + expected throughput due to those APs not enabling aggregation + + If unsure, say N. + menu "Debugging Options" config IWLWIFI_DEBUG diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 3255a1723d17..d1ce3ce13591 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -580,7 +580,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, * time, or we hadn't time to drain the AC queues. */ if (agg_state == IWL_AGG_ON) - iwl_trans_txq_disable(priv->trans, txq_id); + iwl_trans_txq_disable(priv->trans, txq_id, true); else IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", agg_state); @@ -686,7 +686,7 @@ int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif, * time, or we hadn't time to drain the AC queues. */ if (agg_state == IWL_AGG_ON) - iwl_trans_txq_disable(priv->trans, txq_id); + iwl_trans_txq_disable(priv->trans, txq_id, true); else IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", agg_state); @@ -781,7 +781,7 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); iwl_trans_txq_disable(priv->trans, - tid_data->agg.txq_id); + tid_data->agg.txq_id, true); iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); tid_data->agg.state = IWL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index d67a37a786aa..7e26d0dbfcf7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index e93c6972290b..23a67bfc086f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index fe129c94ae3e..23d059af6476 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 295083510e72..0a70bcd241f5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -145,6 +145,7 @@ do { \ #define IWL_DL_HCMD 0x00000004 #define IWL_DL_STATE 0x00000008 /* 0x000000F0 - 0x00000010 */ +#define IWL_DL_QUOTA 0x00000010 #define IWL_DL_TE 0x00000020 #define IWL_DL_EEPROM 0x00000040 #define IWL_DL_RADIO 0x00000080 @@ -189,6 +190,7 @@ do { \ #define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) #define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) #define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) +#define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a) #define IWL_DEBUG_TE(p, f, a...) IWL_DEBUG(p, IWL_DL_TE, f, ## a) #define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a) #define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c index 23e7351e02de..90987d6f348e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -36,15 +36,8 @@ EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_tx); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_info); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_warn); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_crit); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_err); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dbg); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 77e3178040b2..aefd94cb6e91 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1254,7 +1256,9 @@ struct iwl_mod_params iwlwifi_mod_params = { .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .wd_disable = true, - .uapsd_disable = false, +#ifndef CONFIG_IWLWIFI_UAPSD + .uapsd_disable = true, +#endif /* CONFIG_IWLWIFI_UAPSD */ /* the rest are 0 by default */ }; IWL_EXPORT_SYMBOL(iwlwifi_mod_params); @@ -1370,7 +1374,11 @@ MODULE_PARM_DESC(nvm_file, "NVM file name"); module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, bool, S_IRUGO); +#ifdef CONFIG_IWLWIFI_UAPSD MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)"); +#else +MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: Y)"); +#endif /* * set bt_coex_active to true, uCode will do kill/defer diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 3c72cb710b0c..be4f8972241a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index de5994a776c7..e30a41d04c8b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 929a8063354c..401f7be36b93 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 1bb5193c5b1b..f68cba4e0444 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -125,6 +127,8 @@ enum iwl_ucode_tlv_flag { * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API. + * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time + * longer than the passive one, which is essential for fragmented scan. */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), @@ -133,6 +137,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6), + IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8), }; /** diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 018af2957d3b..8e7af798abd1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 99785c892f96..b6d666ee8359 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 47033a35a402..1560f4576c7d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -281,6 +283,7 @@ #define SCD_CHAINEXT_EN (SCD_BASE + 0x244) #define SCD_AGGR_SEL (SCD_BASE + 0x248) #define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) +#define SCD_EN_CTRL (SCD_BASE + 0x254) static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) { diff --git a/drivers/net/wireless/iwlwifi/iwl-scd.h b/drivers/net/wireless/iwlwifi/iwl-scd.h new file mode 100644 index 000000000000..6c622b21bba7 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-scd.h @@ -0,0 +1,118 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_scd_h__ +#define __iwl_scd_h__ + +#include "iwl-trans.h" +#include "iwl-io.h" +#include "iwl-prph.h" + + +static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| + (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + +static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); +} + +static inline void iwl_scd_txq_enable_agg(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); +} + +static inline void iwl_scd_txq_disable_agg(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); +} + +static inline void iwl_scd_disable_agg(struct iwl_trans *trans) +{ + iwl_set_bits_prph(trans, SCD_AGGR_SEL, 0); +} + +static inline void iwl_scd_activate_fifos(struct iwl_trans *trans) +{ + iwl_write_prph(trans, SCD_TXFACT, IWL_MASK(0, 7)); +} + +static inline void iwl_scd_deactivate_fifos(struct iwl_trans *trans) +{ + iwl_write_prph(trans, SCD_TXFACT, 0); +} + +static inline void iwl_scd_enable_set_active(struct iwl_trans *trans, + u32 value) +{ + iwl_write_prph(trans, SCD_EN_CTRL, value); +} +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 656371a668da..c89985a58803 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -401,6 +403,14 @@ struct iwl_trans_dump_data { struct iwl_trans; +struct iwl_trans_txq_scd_cfg { + u8 fifo; + s8 sta_id; + u8 tid; + bool aggregate; + int frame_limit; +}; + /** * struct iwl_trans_ops - transport specific operations * @@ -437,7 +447,9 @@ struct iwl_trans; * Must be atomic * @txq_enable: setup a queue. To setup an AC queue, use the * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before - * this one. The op_mode must not configure the HCMD queue. May sleep. + * this one. The op_mode must not configure the HCMD queue. The scheduler + * configuration may be %NULL, in which case the hardware will not be + * configured. May sleep. * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. @@ -492,9 +504,10 @@ struct iwl_trans_ops { void (*reclaim)(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs); - void (*txq_enable)(struct iwl_trans *trans, int queue, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn); - void (*txq_disable)(struct iwl_trans *trans, int queue); + void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg); + void (*txq_disable)(struct iwl_trans *trans, int queue, + bool configure_scd); int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); @@ -766,29 +779,57 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, trans->ops->reclaim(trans, queue, ssn, skbs); } -static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue) +static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue, + bool configure_scd) { - trans->ops->txq_disable(trans, queue); + trans->ops->txq_disable(trans, queue, configure_scd); } -static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, - int fifo, int sta_id, int tid, - int frame_limit, u16 ssn) +static inline void +iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg) { might_sleep(); if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - trans->ops->txq_enable(trans, queue, fifo, sta_id, tid, - frame_limit, ssn); + trans->ops->txq_enable(trans, queue, ssn, cfg); +} + +static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, + int fifo, int sta_id, int tid, + int frame_limit, u16 ssn) +{ + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = fifo, + .sta_id = sta_id, + .tid = tid, + .frame_limit = frame_limit, + .aggregate = sta_id >= 0, + }; + + iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg); } static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo) { - iwl_trans_txq_enable(trans, queue, fifo, -1, - IWL_MAX_TID_COUNT, IWL_FRAME_LIMIT, 0); + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = fifo, + .sta_id = -1, + .tid = IWL_MAX_TID_COUNT, + .frame_limit = IWL_FRAME_LIMIT, + .aggregate = false, + }; + + iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg); +} + +static inline void +iwl_trans_txq_enable_no_scd(struct iwl_trans *trans, int queue, u16 ssn) +{ + iwl_trans_txq_enable_cfg(trans, queue, ssn, NULL); } static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 2291bbcaaeab..2262d6dc61ae 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index a3be33359927..585c0ab4a3ec 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index ca79f7160573..dd00e8f7f765 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 645b3cfc29a5..c17be0fb7283 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -700,7 +702,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return ret; rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); if (ret) return ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 2e90ff795c13..d919b4ebc83c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -119,6 +121,10 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); dbgfs_pm->uapsd_misbehaving = val; break; + case MVM_DEBUGFS_PM_USE_PS_POLL: + IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val); + dbgfs_pm->use_ps_poll = val; + break; } } @@ -169,6 +175,10 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, if (sscanf(buf + 18, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; + } else if (!strncmp("use_ps_poll=", buf, 12)) { + if (sscanf(buf + 12, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_USE_PS_POLL; } else { return -EINVAL; } diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 7d18f466fbb3..d98ee109c5e9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -257,6 +259,70 @@ static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, return count; } +static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[16]; + int pos; + + if (!mvm->temperature_test) + pos = scnprintf(buf , sizeof(buf), "disabled\n"); + else + pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +/* + * Set NIC Temperature + * Cause the driver to ignore the actual NIC temperature reported by the FW + * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN - + * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX + * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE + */ +static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int temperature; + + if (kstrtoint(buf, 10, &temperature)) + return -EINVAL; + /* not a legal temperature */ + if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX && + temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) || + temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN) + return -EINVAL; + + mutex_lock(&mvm->mutex); + if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) { + if (!mvm->temperature_test) + goto out; + + mvm->temperature_test = false; + /* Since we can't read the temp while awake, just set + * it to zero until we get the next RX stats from the + * firmware. + */ + mvm->temperature = 0; + } else { + mvm->temperature_test = true; + mvm->temperature = temperature; + } + IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n", + mvm->temperature_test ? "En" : "Dis" , + mvm->temperature); + /* handle the temperature change */ + iwl_mvm_tt_handler(mvm); + +out: + mutex_unlock(&mvm->mutex); + + return count; +} + static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1296,6 +1362,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64); MVM_DEBUGFS_READ_FILE_OPS(stations); MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); @@ -1336,6 +1403,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, + S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(fw_error_dump, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); @@ -1380,6 +1449,13 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) goto err; #endif + if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR, + mvm->debugfs_dir, + &mvm->low_latency_agg_frame_limit)) + goto err; + if (!debugfs_create_u8("ps_disabled", S_IRUSR, + mvm->debugfs_dir, &mvm->ps_disabled)) + goto err; if (!debugfs_create_blob("nvm_hw", S_IRUSR, mvm->debugfs_dir, &mvm->nvm_hw_blob)) goto err; diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/iwlwifi/mvm/debugfs.h index e3a9774af495..8c4190e7e027 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.h +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 69875716dcdb..816883f9ff94 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 13696fe419b7..e74cdf2132f8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index c3a8c86b550d..27dd86395b39 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index c02a9e45ec5e..8f2216694004 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 47bd0406355d..21dd5b771660 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 95f5b3274efb..9c975f9ecfcb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,16 +75,20 @@ #include "fw-api-coex.h" #include "fw-api-scan.h" -/* maximal number of Tx queues in any platform */ -#define IWL_MVM_MAX_QUEUES 20 - /* Tx queue numbers */ enum { IWL_MVM_OFFCHANNEL_QUEUE = 8, IWL_MVM_CMD_QUEUE = 9, }; -#define IWL_MVM_CMD_FIFO 7 +enum iwl_mvm_tx_fifo { + IWL_MVM_TX_FIFO_BK = 0, + IWL_MVM_TX_FIFO_BE, + IWL_MVM_TX_FIFO_VI, + IWL_MVM_TX_FIFO_VO, + IWL_MVM_TX_FIFO_MCAST = 5, + IWL_MVM_TX_FIFO_CMD = 7, +}; #define IWL_MVM_STATION_COUNT 16 @@ -184,6 +190,8 @@ enum { REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, + MARKER_CMD = 0xcb, + /* BT Coex */ BT_COEX_PRIO_TABLE = 0xcc, BT_COEX_PROT_ENV = 0xcd, @@ -1307,6 +1315,38 @@ struct iwl_bcast_filter_cmd { struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; } __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ +/* + * enum iwl_mvm_marker_id - maker ids + * + * The ids for different type of markers to insert into the usniffer logs + */ +enum iwl_mvm_marker_id { + MARKER_ID_TX_FRAME_LATENCY = 1, +}; /* MARKER_ID_API_E_VER_1 */ + +/** + * struct iwl_mvm_marker - mark info into the usniffer logs + * + * (MARKER_CMD = 0xcb) + * + * Mark the UTC time stamp into the usniffer logs together with additional + * metadata, so the usniffer output can be parsed. + * In the command response the ucode will return the GP2 time. + * + * @dw_len: The amount of dwords following this byte including this byte. + * @marker_id: A unique marker id (iwl_mvm_marker_id). + * @reserved: reserved. + * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC + * @metadata: additional meta data that will be written to the unsiffer log + */ +struct iwl_mvm_marker { + u8 dwLen; + u8 markerId; + __le16 reserved; + __le64 timestamp; + __le32 metadata[0]; +} __packed; /* MARKER_API_S_VER_1 */ + struct mvm_statistics_dbg { __le32 burst_check; __le32 burst_count; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 883e702152d5..21d606028ca6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -242,10 +244,10 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, mvm->queue_to_mac80211[i] = i; else mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; - atomic_set(&mvm->queue_stop_count[i], 0); } - mvm->transport_queue_stop = 0; + for (i = 0; i < IEEE80211_MAX_QUEUES; i++) + atomic_set(&mvm->mac80211_queue_stop_count[i], 0); mvm->ucode_loaded = true; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 0e523e28cabf..9cbb192f680e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,7 +83,7 @@ struct iwl_mvm_mac_iface_iterator_data { struct ieee80211_vif *vif; unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)]; unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)]; - unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_MAX_QUEUES)]; + u32 used_hw_queues; enum iwl_tsf_id preferred_tsf; bool found_vif; }; @@ -192,12 +194,31 @@ static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac, data->preferred_tsf = NUM_TSF_IDS; } +/* + * Get the mask of the queues used by the vif + */ +u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + u32 qmask = 0, ac; + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + return BIT(IWL_MVM_OFFCHANNEL_QUEUE); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + qmask |= BIT(vif->hw_queue[ac]); + + if (vif->type == NL80211_IFTYPE_AP) + qmask |= BIT(vif->cab_queue); + + return qmask; +} + static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_mac_iface_iterator_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 ac; /* Iterator may already find the interface being added -- skip it */ if (vif == data->vif) { @@ -206,12 +227,7 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, } /* Mark the queues used by the vif */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) - __set_bit(vif->hw_queue[ac], data->used_hw_queues); - - if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) - __set_bit(vif->cab_queue, data->used_hw_queues); + data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(data->mvm, vif); /* Mark MAC IDs as used by clearing the available bit, and * (below) mark TSFs as used if their existing use is not @@ -225,24 +241,6 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, iwl_mvm_mac_tsf_id_iter(_data, mac, vif); } -/* - * Get the mask of the queus used by the vif - */ -u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - u32 qmask = 0, ac; - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) - return BIT(IWL_MVM_OFFCHANNEL_QUEUE); - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) - qmask |= BIT(vif->hw_queue[ac]); - - return qmask; -} - void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -277,15 +275,15 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, /* no preference yet */ .preferred_tsf = NUM_TSF_IDS, - .used_hw_queues = { + .used_hw_queues = BIT(IWL_MVM_OFFCHANNEL_QUEUE) | BIT(mvm->aux_queue) | - BIT(IWL_MVM_CMD_QUEUE) - }, + BIT(IWL_MVM_CMD_QUEUE), .found_vif = false, }; u32 ac; int ret, i; + unsigned long used_hw_queues; /* * Allocate a MAC ID and a TSF for this MAC, along with the queues @@ -368,9 +366,11 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, return 0; } + used_hw_queues = data.used_hw_queues; + /* Find available queues, and allocate them to the ACs */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - u8 queue = find_first_zero_bit(data.used_hw_queues, + u8 queue = find_first_zero_bit(&used_hw_queues, mvm->first_agg_queue); if (queue >= mvm->first_agg_queue) { @@ -379,13 +379,13 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, goto exit_fail; } - __set_bit(queue, data.used_hw_queues); + __set_bit(queue, &used_hw_queues); vif->hw_queue[ac] = queue; } /* Allocate the CAB queue for softAP and GO interfaces */ if (vif->type == NL80211_IFTYPE_AP) { - u8 queue = find_first_zero_bit(data.used_hw_queues, + u8 queue = find_first_zero_bit(&used_hw_queues, mvm->first_agg_queue); if (queue >= mvm->first_agg_queue) { @@ -452,14 +452,16 @@ void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: - iwl_trans_txq_disable(mvm->trans, IWL_MVM_OFFCHANNEL_QUEUE); + iwl_trans_txq_disable(mvm->trans, IWL_MVM_OFFCHANNEL_QUEUE, + true); break; case NL80211_IFTYPE_AP: - iwl_trans_txq_disable(mvm->trans, vif->cab_queue); + iwl_trans_txq_disable(mvm->trans, vif->cab_queue, true); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_trans_txq_disable(mvm->trans, vif->hw_queue[ac]); + iwl_trans_txq_disable(mvm->trans, vif->hw_queue[ac], + true); } } @@ -586,6 +588,7 @@ static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_ctx_cmd *cmd, + const u8 *bssid_override, u32 action) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -593,6 +596,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION); u8 cck_ack_rates, ofdm_ack_rates; + const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; int i; cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, @@ -625,8 +629,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id); memcpy(cmd->node_addr, vif->addr, ETH_ALEN); - if (vif->bss_conf.bssid) - memcpy(cmd->bssid_addr, vif->bss_conf.bssid, ETH_ALEN); + + if (bssid) + memcpy(cmd->bssid_addr, bssid, ETH_ALEN); else eth_broadcast_addr(cmd->bssid_addr); @@ -695,7 +700,8 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 action, bool force_assoc_off) + u32 action, bool force_assoc_off, + const u8 *bssid_override) { struct iwl_mac_ctx_cmd cmd = {}; struct iwl_mac_data_sta *ctxt_sta; @@ -703,7 +709,7 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_STATION); /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, bssid_override, action); if (vif->p2p) { struct ieee80211_p2p_noa_attr *noa = @@ -784,7 +790,7 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | MAC_FILTER_IN_CONTROL_AND_MGMT | @@ -805,7 +811,7 @@ static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | MAC_FILTER_IN_PROBE_REQUEST); @@ -844,7 +850,7 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); @@ -1072,7 +1078,7 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); /* * pass probe requests and beacons from other APs (needed @@ -1098,7 +1104,7 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p); /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); /* * pass probe requests and beacons from other APs (needed @@ -1121,12 +1127,14 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, } static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 action, bool force_assoc_off) + u32 action, bool force_assoc_off, + const u8 *bssid_override) { switch (vif->type) { case NL80211_IFTYPE_STATION: return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action, - force_assoc_off); + force_assoc_off, + bssid_override); break; case NL80211_IFTYPE_AP: if (!vif->p2p) @@ -1157,7 +1165,7 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return -EIO; ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, - true); + true, NULL); if (ret) return ret; @@ -1169,7 +1177,7 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) } int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool force_assoc_off) + bool force_assoc_off, const u8 *bssid_override) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -1178,7 +1186,7 @@ int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return -EIO; return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, - force_assoc_off); + force_assoc_off, bssid_override); } int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7c8796584c25..8d1d4b40b0a3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -776,6 +778,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) iwl_trans_stop_device(mvm->trans); mvm->scan_status = IWL_MVM_SCAN_NONE; + mvm->ps_disabled = false; /* just in case one was running */ ieee80211_remain_on_channel_expired(mvm->hw); @@ -803,6 +806,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) * ucode_down ref until reconfig is complete */ iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); + /* clear any stale d0i3 state */ + clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + mvm->vif_count = 0; mvm->rx_ba_sessions = 0; } @@ -880,7 +886,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) /* async_handlers_list is empty and will stay empty: HW is stopped */ /* the fw is stopped, the aux sta is dead: clean up driver state */ - iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); + iwl_mvm_del_aux_sta(mvm); mutex_unlock(&mvm->mutex); @@ -965,10 +971,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) { - u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); - ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, - qmask, - ieee80211_vif_type_p2p(vif)); + ret = iwl_mvm_alloc_bcast_sta(mvm, vif); if (ret) { IWL_ERR(mvm, "Failed to allocate bcast sta\n"); goto out_release; @@ -1016,7 +1019,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_unref_phy; - ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta); + ret = iwl_mvm_add_bcast_sta(mvm, vif); if (ret) goto out_unbind; @@ -1057,14 +1060,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - u32 tfd_msk = 0, ac; - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) - tfd_msk |= BIT(vif->hw_queue[ac]); - - if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) - tfd_msk |= BIT(vif->cab_queue); + u32 tfd_msk = iwl_mvm_mac_get_queues_mask(mvm, vif); if (tfd_msk) { mutex_lock(&mvm->mutex); @@ -1120,13 +1116,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->noa_duration = 0; } #endif - iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); + iwl_mvm_dealloc_bcast_sta(mvm, vif); goto out_release; } if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { mvm->p2p_device_vif = NULL; - iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta); + iwl_mvm_rm_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); mvmvif->phy_ctxt = NULL; @@ -1445,10 +1441,23 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); + /* + * If we're not associated yet, take the (new) BSSID before associating + * so the firmware knows. If we're already associated, then use the old + * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC + * branch for disassociation below. + */ + if (changes & BSS_CHANGED_BSSID && !mvmvif->associated) + memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); + + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid); if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + /* after sending it once, adopt mac80211 data */ + memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); + mvmvif->associated = bss_conf->assoc; + if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { /* add quota for this interface */ @@ -1476,13 +1485,17 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ u32 dur = (11 * vif->bss_conf.beacon_int) / 10; iwl_mvm_protect_session(mvm, vif, dur, dur, - 5 * dur); + 5 * dur, false); } iwl_mvm_sf_update(mvm, vif, false); iwl_mvm_power_vif_assoc(mvm, vif); - if (vif->p2p) + if (vif->p2p) { iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT); + iwl_mvm_update_smps(mvm, vif, + IWL_MVM_SMPS_REQ_PROT, + IEEE80211_SMPS_DYNAMIC); + } } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { /* * If update fails - SF might be running in associated @@ -1506,6 +1519,13 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (vif->p2p) iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT); + + /* this will take the cleared BSSID from bss_conf */ + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + if (ret) + IWL_ERR(mvm, + "failed to update MAC %pM (clear after unassoc)\n", + vif->addr); } iwl_mvm_recalc_multicast(mvm); @@ -1601,7 +1621,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* Send the bcast station. At this stage the TBTT and DTIM time events * are added and applied to the scheduler */ - ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta); + ret = iwl_mvm_send_add_bcast_sta(mvm, vif); if (ret) goto out_unbind; @@ -1617,7 +1637,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); @@ -1633,7 +1653,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, out_quota_failed: iwl_mvm_power_update_mac(mvm); mvmvif->ap_ibss_active = false; - iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); + iwl_mvm_send_rm_bcast_sta(mvm, vif); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); out_remove: @@ -1675,10 +1695,10 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); iwl_mvm_update_quotas(mvm, NULL); - iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); + iwl_mvm_send_rm_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_power_update_mac(mvm); @@ -1702,7 +1722,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | BSS_CHANGED_BANDWIDTH) && - iwl_mvm_mac_ctxt_changed(mvm, vif, false)) + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL)) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); /* Need to send a new beacon template to the FW */ @@ -2113,7 +2133,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); mutex_unlock(&mvm->mutex); return ret; } @@ -2141,7 +2161,7 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); /* Try really hard to protect the session and hear a beacon */ - iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500); + iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); mutex_unlock(&mvm->mutex); iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX); @@ -2162,7 +2182,7 @@ static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); /* Protect the session to hear the TDLS setup response on the channel */ - iwl_mvm_protect_session(mvm, vif, duration, duration, 100); + iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true); mutex_unlock(&mvm->mutex); iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS); @@ -2700,7 +2720,10 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, ret = 0; goto out; case NL80211_IFTYPE_STATION: + break; case NL80211_IFTYPE_MONITOR: + /* always disable PS when a monitor interface is active */ + mvmvif->ps_disabled = true; break; default: ret = -EINVAL; @@ -2732,7 +2755,20 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, if ((vif->type == NL80211_IFTYPE_AP) || (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) { iwl_mvm_update_quotas(mvm, NULL); - iwl_mvm_mac_ctxt_changed(mvm, vif, false); + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + } + + if (vif->csa_active && vif->type == NL80211_IFTYPE_STATION) { + struct iwl_mvm_sta *mvmsta; + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, + mvmvif->ap_sta_id); + + if (WARN_ON(!mvmsta)) + goto out; + + /* TODO: only re-enable after the first beacon */ + iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); } goto out; @@ -2766,6 +2802,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_vif *disabled_vif = NULL; + struct iwl_mvm_sta *mvmsta; lockdep_assert_held(&mvm->mutex); @@ -2776,6 +2813,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, goto out; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; + mvmvif->ps_disabled = false; break; case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ @@ -2796,7 +2834,13 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, disabled_vif = vif; - iwl_mvm_mac_ctxt_changed(mvm, vif, true); + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, + mvmvif->ap_sta_id); + + if (!WARN_ON(!mvmsta)) + iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); + + iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); break; default: break; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 2e73d3bd7757..e292de96e09a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -103,14 +105,6 @@ */ #define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3 -enum iwl_mvm_tx_fifo { - IWL_MVM_TX_FIFO_BK = 0, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_VI, - IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_MCAST = 5, -}; - extern const struct ieee80211_ops iwl_mvm_hw_ops; /** @@ -203,6 +197,7 @@ enum iwl_dbgfs_pm_mask { MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9), + MVM_DEBUGFS_PM_USE_PS_POLL = BIT(10), }; struct iwl_dbgfs_pm { @@ -215,6 +210,7 @@ struct iwl_dbgfs_pm { u32 lprx_rssi_threshold; bool snooze_ena; bool uapsd_misbehaving; + bool use_ps_poll; int mask; }; @@ -253,6 +249,7 @@ struct iwl_dbgfs_bf { enum iwl_mvm_smps_type_request { IWL_MVM_SMPS_REQ_BT_COEX, IWL_MVM_SMPS_REQ_TT, + IWL_MVM_SMPS_REQ_PROT, NUM_IWL_MVM_SMPS_REQ, }; @@ -315,6 +312,9 @@ struct iwl_mvm_vif_bf_data { * @id: between 0 and 3 * @color: to solve races upon MAC addition and removal * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA + * @bssid: BSSID for this (client) interface + * @associated: indicates that we're currently associated, used only for + * managing the firmware state in iwl_mvm_bss_info_changed_station() * @uploaded: indicates the MAC context has been added to the device * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface * should get quota etc. @@ -323,6 +323,7 @@ struct iwl_mvm_vif_bf_data { * interface should get quota etc. * @low_latency: indicates that this interface is in low-latency mode * (VMACLowLatencyMode) + * @ps_disabled: indicates that this interface requires PS to be disabled * @queue_params: QoS params for this MAC * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. @@ -335,11 +336,15 @@ struct iwl_mvm_vif { u16 color; u8 ap_sta_id; + u8 bssid[ETH_ALEN]; + bool associated; + bool uploaded; bool ap_ibss_active; bool pm_enabled; bool monitor_active; bool low_latency; + bool ps_disabled; struct iwl_mvm_vif_bf_data bf_data; u32 ap_beacon_time; @@ -512,6 +517,10 @@ enum { D0I3_PENDING_WAKEUP, }; +#define IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE 0xff +#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100 +#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200 + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -553,9 +562,8 @@ struct iwl_mvm { struct mvm_statistics_rx rx_stats; - unsigned long transport_queue_stop; u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; - atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; + atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; const char *nvm_file_name; struct iwl_nvm_data *nvm_data; @@ -694,6 +702,12 @@ struct iwl_mvm { /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; s32 temperature; /* Celsius */ + /* + * Debug option to set the NIC temperature. This option makes the + * driver think this is the actual NIC temperature, and ignore the + * real temperature that is received from the fw + */ + bool temperature_test; /* Debug test temperature is enabled */ #ifdef CONFIG_NL80211_TESTMODE u32 noa_duration; @@ -706,7 +720,7 @@ struct iwl_mvm { u8 last_agg_queue; /* Indicate if device power save is allowed */ - bool ps_disabled; + u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ struct ieee80211_vif __rcu *csa_vif; struct ieee80211_vif __rcu *csa_tx_blocked_vif; @@ -714,6 +728,8 @@ struct iwl_mvm { /* system time of last beacon (for AP/GO interface) */ u32 ap_last_beacon_gp2; + + u8 low_latency_agg_frame_limit; }; /* Extract MVM priv from op_mode and _hw */ @@ -878,7 +894,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool force_assoc_off); + bool force_assoc_off, const u8 *bssid_override); int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -968,6 +984,7 @@ int rs_pretty_print_rate(char *buf, const u32 rate); /* power management */ int iwl_mvm_power_update_device(struct iwl_mvm *mvm); int iwl_mvm_power_update_mac(struct iwl_mvm *mvm); +int iwl_mvm_power_update_ps(struct iwl_mvm *mvm); int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, char *buf, int bufsz); diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index cfdd314fdd5d..4fafd4bd89f4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c index 9bfb95e89cfb..adcbf4c8edd8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/offloading.c +++ b/drivers/net/wireless/iwlwifi/mvm/offloading.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 610dbcb0dc27..87f278cc9b2c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -415,6 +417,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->first_agg_queue = 12; } mvm->sf_state = SF_UNINIT; + mvm->low_latency_agg_frame_limit = 1; mutex_init(&mvm->mutex); mutex_init(&mvm->d0i3_suspend_mutex); @@ -456,7 +459,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.command_names = iwl_mvm_cmd_strings; trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; - trans_cfg.cmd_fifo = IWL_MVM_CMD_FIFO; + trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), @@ -494,7 +497,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, goto out_free; /* - * Even if nvm exists in the nvm_file driver should read agin the nvm + * Even if nvm exists in the nvm_file driver should read again the nvm * from the nic because there might be entries that exist in the OTP * and not in the file. * for nics with no_power_up_nic_in_init: rely completley on nvm_file @@ -700,14 +703,13 @@ static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) return; - if (atomic_inc_return(&mvm->queue_stop_count[mq]) > 1) { + if (atomic_inc_return(&mvm->mac80211_queue_stop_count[mq]) > 1) { IWL_DEBUG_TX_QUEUES(mvm, "queue %d (mac80211 %d) already stopped\n", queue, mq); return; } - set_bit(mq, &mvm->transport_queue_stop); ieee80211_stop_queue(mvm->hw, mq); } @@ -719,15 +721,13 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) return; - if (atomic_dec_return(&mvm->queue_stop_count[mq]) > 0) { + if (atomic_dec_return(&mvm->mac80211_queue_stop_count[mq]) > 0) { IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) already awake\n", + "queue %d (mac80211 %d) still stopped\n", queue, mq); return; } - clear_bit(mq, &mvm->transport_queue_stop); - ieee80211_wake_queue(mvm->hw, mq); } diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 6cc243f7cf60..12283b55ee84 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 2b2d10800a55..e7a6626fe839 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -198,8 +200,15 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, } } - if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) + if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* set advanced pm flag with no uapsd ACs to enable ps-poll */ + if (mvmvif->dbgfs_pm.use_ps_poll) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); +#endif return; + } cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); @@ -497,13 +506,31 @@ struct iwl_power_vifs { bool p2p_tdls; }; -static void iwl_mvm_power_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) +static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->pm_enabled = false; +} + +static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *disable_ps = _data; + + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + *disable_ps |= mvmvif->ps_disabled; +} + +static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_power_vifs *power_iterator = _data; - mvmvif->pm_enabled = false; switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_P2P_DEVICE: break; @@ -559,9 +586,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac, } } -static void -iwl_mvm_power_set_pm(struct iwl_mvm *mvm, - struct iwl_power_vifs *vifs) +static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, + struct iwl_power_vifs *vifs) { struct iwl_mvm_vif *bss_mvmvif = NULL; struct iwl_mvm_vif *p2p_mvmvif = NULL; @@ -571,10 +597,11 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - /* get vifs info + set pm_enable to false */ + /* set pm_enable to false */ ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_iterator, vifs); + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_disable_pm_iterator, + NULL); if (vifs->bss_vif) bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); @@ -817,32 +844,92 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, return ret; } -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) +static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) +{ + bool disable_ps; + int ret; + + /* disable PS if CAM */ + disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); + /* ...or if any of the vifs require PS to be off */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_ps_disabled_iterator, + &disable_ps); + + /* update device power state if it has changed */ + if (mvm->ps_disabled != disable_ps) { + bool old_ps_disabled = mvm->ps_disabled; + + mvm->ps_disabled = disable_ps; + ret = iwl_mvm_power_update_device(mvm); + if (ret) { + mvm->ps_disabled = old_ps_disabled; + return ret; + } + } + + return 0; +} + +static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, + struct iwl_power_vifs *vifs) { struct iwl_mvm_vif *mvmvif; + bool ba_enable; + + if (!vifs->bf_vif) + return 0; + + mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif); + + ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || + !vifs->bf_vif->bss_conf.ps || + iwl_mvm_vif_low_latency(mvmvif)); + + return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable); +} + +int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) +{ struct iwl_power_vifs vifs = { .mvm = mvm, }; - bool ba_enable; int ret; lockdep_assert_held(&mvm->mutex); + /* get vifs info */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_get_vifs_iterator, &vifs); + + ret = iwl_mvm_power_set_ps(mvm); + if (ret) + return ret; + + return iwl_mvm_power_set_ba(mvm, &vifs); +} + +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) +{ + struct iwl_power_vifs vifs = { + .mvm = mvm, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* get vifs info */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_get_vifs_iterator, &vifs); + iwl_mvm_power_set_pm(mvm, &vifs); - /* disable PS if CAM */ - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) { - mvm->ps_disabled = true; - } else { - /* don't update device power state unless we add / remove monitor */ - if (vifs.monitor_vif) { - if (vifs.monitor_active) - mvm->ps_disabled = true; - ret = iwl_mvm_power_update_device(mvm); - if (ret) - return ret; - } - } + ret = iwl_mvm_power_set_ps(mvm); + if (ret) + return ret; if (vifs.bss_vif) { ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); @@ -856,16 +943,7 @@ int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) return ret; } - if (!vifs.bf_vif) - return 0; - - mvmvif = iwl_mvm_vif_from_mac80211(vifs.bf_vif); - - ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || - !vifs.bf_vif->bss_conf.ps || - iwl_mvm_vif_low_latency(mvmvif)); - - return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable); + return iwl_mvm_power_set_ba(mvm, &vifs); } int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 4e20b3ce2b6a..5fd502db03d1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -161,6 +163,9 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, quota *= (beacon_int - mvm->noa_duration); quota /= beacon_int; + IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n", + le32_to_cpu(cmd->quotas[i].quota), quota); + cmd->quotas[i].quota = cpu_to_le32(quota); } #endif @@ -222,6 +227,9 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat; quota_rem = QUOTA_100 - n_non_lowlat * quota - QUOTA_LOWLAT_MIN; + IWL_DEBUG_QUOTA(mvm, + "quota: low-latency binding active, remaining quota per other binding: %d\n", + quota); } else if (num_active_macs) { /* * There are 0 or more than 1 low latency bindings, or all the @@ -230,6 +238,9 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, */ quota = QUOTA_100 / num_active_macs; quota_rem = QUOTA_100 % num_active_macs; + IWL_DEBUG_QUOTA(mvm, + "quota: splitting evenly per binding: %d\n", + quota); } else { /* values don't really matter - won't be used */ quota = 0; @@ -271,6 +282,9 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, for (i = 0; i < MAX_BINDINGS; i++) { if (le32_to_cpu(cmd.quotas[i].quota) != 0) { le32_add_cpu(&cmd.quotas[i].quota, quota_rem); + IWL_DEBUG_QUOTA(mvm, + "quota: giving remainder of %d to binding %d\n", + quota_rem, i); break; } } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index c70e959bf0e3..17002cf437db 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -2678,6 +2679,7 @@ static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, int i; int num_rates = ARRAY_SIZE(lq_cmd->rs_table); __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); + u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; for (i = 0; i < num_rates; i++) lq_cmd->rs_table[i] = ucode_rate_le32; @@ -2688,6 +2690,13 @@ static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, lq_cmd->mimo_delim = num_rates - 1; else lq_cmd->mimo_delim = 0; + + lq_cmd->reduced_tpc = 0; + + if (num_of_ant(ant) == 1) + lq_cmd->single_stream_ant_msk = ant; + + lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; } #endif /* CONFIG_MAC80211_DEBUGFS */ @@ -2811,31 +2820,55 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, const struct rs_rate *initial_rate) { struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - u8 ant = initial_rate->ant; + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_vif *mvmvif; + + lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + lq_cmd->agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); #ifdef CONFIG_MAC80211_DEBUGFS if (lq_sta->pers.dbg_fixed_rate) { rs_build_rates_table_from_fixed(mvm, lq_cmd, lq_sta->band, lq_sta->pers.dbg_fixed_rate); - lq_cmd->reduced_tpc = 0; - ant = (lq_sta->pers.dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> - RATE_MCS_ANT_POS; - } else + return; + } #endif - rs_build_rates_table(mvm, lq_sta, initial_rate); + if (WARN_ON_ONCE(!sta || !initial_rate)) + return; - if (num_of_ant(ant) == 1) - lq_cmd->single_stream_ant_msk = ant; + rs_build_rates_table(mvm, lq_sta, initial_rate); - lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + if (num_of_ant(initial_rate->ant) == 1) + lq_cmd->single_stream_ant_msk = initial_rate->ant; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + + if (num_of_ant(initial_rate->ant) == 1) + lq_cmd->single_stream_ant_msk = initial_rate->ant; + + lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; + + /* + * In case of low latency, tell the firwmare to leave a frame in the + * Tx Fifo so that it can start a transaction in the same TxOP. This + * basically allows the firmware to send bursts. + */ + if (iwl_mvm_vif_low_latency(mvmvif)) { + lq_cmd->agg_frame_cnt_limit--; + + if (mvm->low_latency_agg_frame_limit) + lq_cmd->agg_frame_cnt_limit = + min(lq_cmd->agg_frame_cnt_limit, + mvm->low_latency_agg_frame_limit); + } + + if (mvmsta->vif->p2p) + lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK; lq_cmd->agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - if (sta) - lq_cmd->agg_time_limit = cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); } @@ -2932,10 +2965,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate); if (lq_sta->pers.dbg_fixed_rate) { - struct rs_rate rate; - rs_rate_from_ucode_rate(lq_sta->pers.dbg_fixed_rate, - lq_sta->band, &rate); - rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate); + rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL); iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false); } } diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 4b98987fc413..48144e3ad527 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -491,10 +493,29 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, .mvm = mvm, }; + /* + * set temperature debug enabled - ignore FW temperature updates + * and use the user set temperature. + */ + if (mvm->temperature_test) { + if (mvm->temperature < le32_to_cpu(common->temperature)) + IWL_DEBUG_TEMP(mvm, + "Ignoring FW temperature update that is greater than the debug set temperature (debug temp = %d, fw temp = %d)\n", + mvm->temperature, + le32_to_cpu(common->temperature)); + /* + * skip iwl_mvm_tt_handler since we are in + * temperature debug mode and we are ignoring + * the new temperature value + */ + goto update; + } + if (mvm->temperature != le32_to_cpu(common->temperature)) { mvm->temperature = le32_to_cpu(common->temperature); iwl_mvm_tt_handler(mvm); } +update: iwl_mvm_update_rx_statistics(mvm, stats); ieee80211_iterate_active_interfaces(mvm->hw, diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 004b1f5d0314..bf9c63dc4a7d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -279,6 +281,7 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, { bool global_bound = false; enum ieee80211_band band; + u8 frag_passive_dwell = 0; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, @@ -288,12 +291,36 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, if (!global_bound) goto not_bound; - params->suspend_time = 100; - params->max_out_time = 600; + params->suspend_time = 30; + params->max_out_time = 170; if (iwl_mvm_low_latency(mvm)) { - params->suspend_time = 250; - params->max_out_time = 250; + if (mvm->fw->ucode_capa.api[0] & + IWL_UCODE_TLV_API_FRAGMENTED_SCAN) { + params->suspend_time = 105; + params->max_out_time = 70; + frag_passive_dwell = 20; + } else { + params->suspend_time = 120; + params->max_out_time = 120; + } + } + + if (frag_passive_dwell && (mvm->fw->ucode_capa.api[0] & + IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) { + /* + * P2P device scan should not be fragmented to avoid negative + * impact on P2P device discovery. Configure max_out_time to be + * equal to dwell time on passive channel. Take a longest + * possible value, one that corresponds to 2GHz band + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + u32 passive_dwell = + iwl_mvm_get_passive_dwell(IEEE80211_BAND_2GHZ); + params->max_out_time = passive_dwell; + } else { + params->passive_fragmented = true; + } } if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY) @@ -302,7 +329,11 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, not_bound: for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { - params->dwell[band].passive = iwl_mvm_get_passive_dwell(band); + if (params->passive_fragmented) + params->dwell[band].passive = frag_passive_dwell; + else + params->dwell[band].passive = + iwl_mvm_get_passive_dwell(band); params->dwell[band].active = iwl_mvm_get_active_dwell(band, n_ssids); } @@ -1100,10 +1131,11 @@ iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, struct iwl_mvm_scan_params *params) { memset(cmd, 0, ksize(cmd)); - cmd->active_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].active; - cmd->passive_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].passive; - /* TODO: Use params; now fragmented isn't used. */ - cmd->fragmented_dwell = 0; + cmd->active_dwell = params->dwell[IEEE80211_BAND_2GHZ].active; + cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive; + if (params->passive_fragmented) + cmd->fragmented_dwell = + params->dwell[IEEE80211_BAND_2GHZ].passive; cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm); cmd->max_out_time = cpu_to_le32(params->max_out_time); cmd->suspend_time = cpu_to_le32(params->suspend_time); diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c index 7edfd15efc9d..d1922afe06f4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/iwlwifi/mvm/sf.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 763548880399..dd9f3a4347f6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -250,10 +252,14 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (ret) return ret; - /* The first station added is the AP, the others are TDLS STAs */ - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - mvmvif->ap_sta_id = sta_id; + if (vif->type == NL80211_IFTYPE_STATION) { + if (!sta->tdls) { + WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT); + mvmvif->ap_sta_id = sta_id; + } else { + WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT); + } + } rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); @@ -458,8 +464,9 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, return ret; } -int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype) +static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + u32 qmask, enum nl80211_iftype iftype) { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); @@ -474,7 +481,8 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, return 0; } -void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) +static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta) { RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); @@ -544,6 +552,13 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) return ret; } +void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm) +{ + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); +} + /* * Send the add station command for the vif's broadcast station. * Assumes that the station was already allocated. @@ -552,10 +567,10 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) * @vif: the interface to which the broadcast station is added * @bsta: the broadcast station to add. */ -int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *bsta) +int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; const u8 *baddr = _baddr; @@ -573,19 +588,40 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* Send the FW a request to remove the station from it's internal data * structures, but DO NOT remove the entry from the local data structures. */ -int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *bsta) +int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; lockdep_assert_held(&mvm->mutex); - ret = iwl_mvm_rm_sta_common(mvm, bsta->sta_id); + ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); if (ret) IWL_WARN(mvm, "Failed sending remove station\n"); return ret; } +int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 qmask; + + lockdep_assert_held(&mvm->mutex); + + qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); + + /* + * The firmware defines the TFD queue mask to only be relevant + * for *unicast* queues, so the multicast (CAB) queue shouldn't + * be included. + */ + if (vif->type == NL80211_IFTYPE_AP) + qmask &= ~BIT(vif->cab_queue); + + return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask, + ieee80211_vif_type_p2p(vif)); +} + /* Allocate a new station entry for the broadcast station to the given vif, * and send it to the FW. * Note that each P2P mac should have its own broadcast station. @@ -593,45 +629,47 @@ int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, * @mvm: the mvm component * @vif: the interface to which the broadcast station is added * @bsta: the broadcast station to add. */ -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *bsta) +int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - u32 qmask; + struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; int ret; lockdep_assert_held(&mvm->mutex); - qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); - ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask, - ieee80211_vif_type_p2p(vif)); + ret = iwl_mvm_alloc_bcast_sta(mvm, vif); if (ret) return ret; - ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr, - mvmvif->id, mvmvif->color); + ret = iwl_mvm_send_add_bcast_sta(mvm, vif); if (ret) iwl_mvm_dealloc_int_sta(mvm, bsta); + return ret; } +void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); +} + /* * Send the FW a request to remove the station from it's internal data * structures, and in addition remove it from the local data structure. */ -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta) +int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { int ret; lockdep_assert_held(&mvm->mutex); - ret = iwl_mvm_rm_sta_common(mvm, bsta->sta_id); - if (ret) - return ret; + ret = iwl_mvm_send_rm_bcast_sta(mvm, vif); + + iwl_mvm_dealloc_bcast_sta(mvm, vif); - iwl_mvm_dealloc_int_sta(mvm, bsta); return ret; } @@ -910,7 +948,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, } tid_data->ssn = 0xffff; - iwl_trans_txq_disable(mvm->trans, txq_id); + iwl_trans_txq_disable(mvm->trans, txq_id, true); /* fall through */ case IWL_AGG_STARTING: case IWL_EMPTYING_HW_QUEUE_ADDBA: @@ -965,7 +1003,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true)) IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); - iwl_trans_txq_disable(mvm->trans, tid_data->txq_id); + iwl_trans_txq_disable(mvm->trans, tid_data->txq_id, true); } mvm->queue_to_mac80211[tid_data->txq_id] = diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 3b1c8bd6cb54..aeb3a7f80ceb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -387,17 +389,15 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm); -int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype); -void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta); -int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *bsta); -int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *bsta); -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *bsta); -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta); +void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm); + +int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + void iwl_mvm_sta_drained_wk(struct work_struct *wk); void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct ieee80211_sta *sta); diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h index 0241665925f7..79ab6beb6b26 100644 --- a/drivers/net/wireless/iwlwifi/mvm/testmode.h +++ b/drivers/net/wireless/iwlwifi/mvm/testmode.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 33e5041f1efc..447d3b1003df 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -348,6 +350,38 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, return 0; } +static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mvm *mvm = + container_of(notif_wait, struct iwl_mvm, notif_wait); + struct iwl_mvm_time_event_data *te_data = data; + struct iwl_time_event_notif *resp; + int resp_len = iwl_rx_packet_payload_len(pkt); + + if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) + return true; + + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { + IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); + return true; + } + + resp = (void *)pkt->data; + + /* te_data->uid is already set in the TIME_EVENT_CMD response */ + if (le32_to_cpu(resp->unique_id) != te_data->uid) + return false; + + IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", + te_data->uid); + if (!resp->status) + IWL_ERR(mvm, + "TIME_EVENT_NOTIFICATION received but not executed\n"); + + return true; +} + static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { @@ -441,10 +475,12 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, void iwl_mvm_protect_session(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 duration, u32 min_duration, - u32 max_delay) + u32 max_delay, bool wait_for_notif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + const u8 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; + struct iwl_notification_wait wait_te_notif; struct iwl_time_event_cmd time_cmd = {}; lockdep_assert_held(&mvm->mutex); @@ -489,7 +525,28 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, TE_V2_NOTIF_HOST_EVENT_END | T2_V2_START_IMMEDIATELY); - iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); + if (!wait_for_notif) { + iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); + return; + } + + /* + * Create notification_wait for the TIME_EVENT_NOTIFICATION to use + * right after we send the time event + */ + iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, + te_notif_response, + ARRAY_SIZE(te_notif_response), + iwl_mvm_te_notif, te_data); + + /* If TE was sent OK - wait for the notification that started */ + if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { + IWL_ERR(mvm, "Failed to add TE to protect session\n"); + iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); + } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, + TU_TO_JIFFIES(max_delay))) { + IWL_ERR(mvm, "Failed to protect session until TE\n"); + } } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h index 2f48a90d4ad3..bee3b2446b35 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -124,10 +126,12 @@ * @min_duration: will start a new session if the current session will end * in less than min_duration. * @max_delay: maximum delay before starting the time event (in TU) + * @wait_for_notif: true if it is required that a time event notification be + * waited for (that the time event has been scheduled before returning) * * This function can be used to start a session protection which means that the * fw will stay on the channel for %duration_ms milliseconds. This function - * will block (sleep) until the session starts. This function can also be used + * can block (sleep) until the session starts. This function can also be used * to extend a currently running session. * This function is meant to be used for BSS association for example, where we * want to make sure that the fw stays on the channel during the association. @@ -135,7 +139,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 duration, u32 min_duration, - u32 max_delay); + u32 max_delay, bool wait_for_notif); /** * iwl_mvm_stop_session_protection - cancel the session protection. diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 0464599c111e..c3e1fe4282f1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -314,14 +316,26 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) { u32 duration = mvm->thermal_throttle.params->ct_kill_duration; + if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) + return; + IWL_ERR(mvm, "Enter CT Kill\n"); iwl_mvm_set_hw_ctkill_state(mvm, true); - schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, - round_jiffies_relative(duration * HZ)); + + /* Don't schedule an exit work if we're in test mode, since + * the temperature will not change unless we manually set it + * again (or disable testing). + */ + if (!mvm->temperature_test) + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies_relative(duration * HZ)); } static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) { + if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) + return; + IWL_ERR(mvm, "Exit CT Kill\n"); iwl_mvm_set_hw_ctkill_state(mvm, false); } @@ -444,6 +458,12 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm) return; } + if (params->support_ct_kill && + temperature <= tt->params->ct_kill_exit) { + iwl_mvm_exit_ctkill(mvm); + return; + } + if (params->support_dynamic_smps) { if (!tt->dynamic_smps && temperature >= params->dynamic_smps_entry) { diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index dbc870713882..963edb8656ad 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -482,7 +484,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); - iwl_trans_txq_disable(mvm->trans, tid_data->txq_id); + iwl_trans_txq_disable(mvm->trans, tid_data->txq_id, true); tid_data->state = IWL_AGG_OFF; /* * we can't hold the mutex - but since we are after a sequence diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index ac249da8a22b..1958f298ac8b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -387,15 +389,19 @@ struct iwl_error_event_table { struct iwl_umac_error_event_table { u32 valid; /* (nonzero) valid, (0) log is empty */ u32 error_id; /* type of error */ - u32 pc; /* program counter */ u32 blink1; /* branch link */ u32 blink2; /* branch link */ u32 ilink1; /* interrupt link */ u32 ilink2; /* interrupt link */ u32 data1; /* error-specific data */ u32 data2; /* error-specific data */ - u32 line; /* source code line of error */ - u32 umac_ver; /* umac version */ + u32 data3; /* error-specific data */ + u32 umac_fw_ver; /* UMAC version */ + u32 umac_fw_api_ver; /* UMAC FW API ver */ + u32 frame_pointer; /* core register 27*/ + u32 stack_pointer; /* core register 28 */ + u32 cmd_header; /* latest host cmd sent to UMAC */ + u32 nic_isr_pref; /* ISR status register */ } __packed; #define ERROR_START_OFFSET (1 * sizeof(u32)) @@ -409,7 +415,7 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) base = mvm->umac_error_event_table; - if (base < 0x800000 || base >= 0x80C000) { + if (base < 0x800000) { IWL_ERR(mvm, "Not valid error log pointer 0x%08X for %s uCode\n", base, @@ -428,14 +434,19 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc); IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1); IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2); IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver); + IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3); + IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_fw_ver); + IWL_ERR(mvm, "0x%08X | umac api version\n", table.umac_fw_api_ver); + IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer); + IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer); + IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header); + IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref); } void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index f0e722ced080..dbbbf23082a2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 78f72c34438a..a4fedc4a7448 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -364,9 +365,10 @@ int iwl_pcie_tx_init(struct iwl_trans *trans); void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); int iwl_pcie_tx_stop(struct iwl_trans *trans); void iwl_pcie_tx_free(struct iwl_trans *trans); -void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn); -void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg); +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, + bool configure_scd); int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index a2698e5e062c..702f47fb16fe 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 06e04aaf61ee..3076e0e9a490 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 6acccb19c4f3..a6336b4aa3a4 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -34,6 +35,7 @@ #include "iwl-csr.h" #include "iwl-prph.h" #include "iwl-io.h" +#include "iwl-scd.h" #include "iwl-op-mode.h" #include "internal.h" /* FIXME: need to abstract out TX command (once we know what it looks like) */ @@ -644,17 +646,6 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) memset(txq, 0, sizeof(*txq)); } -/* - * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - */ -static void iwl_pcie_txq_set_sched(struct iwl_trans *trans, u32 mask) -{ - struct iwl_trans_pcie __maybe_unused *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - iwl_write_prph(trans, SCD_TXFACT, mask); -} - void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -692,7 +683,7 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) trans_pcie->cmd_fifo); /* Activate all Tx DMA/FIFO channels */ - iwl_pcie_txq_set_sched(trans, IWL_MASK(0, 7)); + iwl_scd_activate_fifos(trans); /* Enable DMA channel */ for (chan = 0; chan < FH_TCSR_CHNL_NUM; chan++) @@ -745,7 +736,7 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans) /* Turn off all Tx DMA fifos */ spin_lock(&trans_pcie->irq_lock); - iwl_pcie_txq_set_sched(trans, 0); + iwl_scd_deactivate_fifos(trans); /* Stop each Tx DMA channel, and wait for it to be idle */ for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { @@ -886,7 +877,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) spin_lock(&trans_pcie->irq_lock); /* Turn off all Tx DMA fifos */ - iwl_write_prph(trans, SCD_TXFACT, 0); + iwl_scd_deactivate_fifos(trans); /* Tell NIC where to find the "keep warm" buffer */ iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, @@ -1072,55 +1063,52 @@ static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, return 0; } -static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans, - u16 txq_id) -{ - /* Simply stop the queue, but don't change any configuration; - * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ - iwl_write_prph(trans, - SCD_QUEUE_STATUS_BITS(txq_id), - (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| - (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); -} - /* Receiver address (actually, Rx station's index into station table), * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ #define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) -void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn) +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int fifo = -1; if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); - /* Stop this Tx queue before configuring it */ - iwl_pcie_txq_set_inactive(trans, txq_id); + if (cfg) { + fifo = cfg->fifo; - /* Set this queue as a chain-building queue unless it is CMD queue */ - if (txq_id != trans_pcie->cmd_queue) - iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); + /* Disable the scheduler prior configuring the cmd queue */ + if (txq_id == trans_pcie->cmd_queue) + iwl_scd_enable_set_active(trans, 0); - /* If this queue is mapped to a certain station: it is an AGG queue */ - if (sta_id >= 0) { - u16 ra_tid = BUILD_RAxTID(sta_id, tid); + /* Stop this Tx queue before configuring it */ + iwl_scd_txq_set_inactive(trans, txq_id); - /* Map receiver-address / traffic-ID to this queue */ - iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id); + /* Set this queue as a chain-building queue unless it is CMD */ + if (txq_id != trans_pcie->cmd_queue) + iwl_scd_txq_set_chain(trans, txq_id); - /* enable aggregations for the queue */ - iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); - trans_pcie->txq[txq_id].ampdu = true; - } else { - /* - * disable aggregations for the queue, this will also make the - * ra_tid mapping configuration irrelevant since it is now a - * non-AGG queue. - */ - iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + if (cfg->aggregate) { + u16 ra_tid = BUILD_RAxTID(cfg->sta_id, cfg->tid); - ssn = trans_pcie->txq[txq_id].q.read_ptr; + /* Map receiver-address / traffic-ID to this queue */ + iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id); + + /* enable aggregations for the queue */ + iwl_scd_txq_enable_agg(trans, txq_id); + trans_pcie->txq[txq_id].ampdu = true; + } else { + /* + * disable aggregations for the queue, this will also + * make the ra_tid mapping configuration irrelevant + * since it is now a non-AGG queue. + */ + iwl_scd_txq_disable_agg(trans, txq_id); + + ssn = trans_pcie->txq[txq_id].q.read_ptr; + } } /* Place first TFD at index corresponding to start sequence number. @@ -1128,32 +1116,43 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (ssn & 0xff) | (txq_id << 8)); - iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); + if (cfg) { + u8 frame_limit = cfg->frame_limit; - /* Set up Tx window size and frame limit for this queue */ - iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); - iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (ssn & 0xff) | (txq_id << 8)); + iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); + + /* Set up Tx window size and frame limit for this queue */ + iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); + iwl_trans_write_mem32(trans, + trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + + /* Set up status area in SRAM, map to Tx DMA/FIFO, activate */ + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (cfg->fifo << SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << SCD_QUEUE_STTS_REG_POS_WSL) | + SCD_QUEUE_STTS_REG_MSK); + + /* enable the scheduler for this queue (only) */ + if (txq_id == trans_pcie->cmd_queue) + iwl_scd_enable_set_active(trans, BIT(txq_id)); + } - /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ - iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), - (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (fifo << SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << SCD_QUEUE_STTS_REG_POS_WSL) | - SCD_QUEUE_STTS_REG_MSK); trans_pcie->txq[txq_id].active = true; IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n", txq_id, fifo, ssn & 0xff); } -void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, + bool configure_scd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 stts_addr = trans_pcie->scd_base_addr + @@ -1172,10 +1171,12 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) return; } - iwl_pcie_txq_set_inactive(trans, txq_id); + if (configure_scd) { + iwl_scd_txq_set_inactive(trans, txq_id); - iwl_trans_write_mem(trans, stts_addr, (void *)zero_val, - ARRAY_SIZE(zero_val)); + iwl_trans_write_mem(trans, stts_addr, (void *)zero_val, + ARRAY_SIZE(zero_val)); + } iwl_pcie_txq_unmap(trans, txq_id); trans_pcie->txq[txq_id].ampdu = false; diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 47a998d8f99e..22884ba7d6cc 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -653,6 +653,7 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, if (channel && !(channel->flags & IEEE80211_CHAN_DISABLED)) { bss = cfg80211_inform_bss(wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, get_unaligned_le64(tsfdesc), capa, intvl, ie, ielen, LBS_SCAN_RSSI_TO_MBM(rssi), @@ -1754,6 +1755,7 @@ static void lbs_join_post(struct lbs_private *priv, bss = cfg80211_inform_bss(priv->wdev->wiphy, params->chandef.chan, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, capability, diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e2e6bf13c2d8..c4723b0f5757 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -246,7 +246,7 @@ mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, } if (priv->roc_cfg.cookie) { - wiphy_dbg(wiphy, "info: ongoing ROC, cookie = 0x%llu\n", + wiphy_dbg(wiphy, "info: ongoing ROC, cookie = 0x%llx\n", priv->roc_cfg.cookie); return -EBUSY; } @@ -1557,6 +1557,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) band)); bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, + CFG80211_BSS_FTYPE_UNKNOWN, bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, 0, ie_buf, ie_len, 0, GFP_KERNEL); cfg80211_put_bss(priv->wdev->wiphy, bss); diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index baf0aab63c04..985f6c2654fb 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1470,7 +1470,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie_types_header *tlv; - struct hw_spec_fw_api_rev *api_rev; + struct hw_spec_api_rev *api_rev; u16 resp_size, api_id; int i, left_len, parsed_len = 0; @@ -1508,7 +1508,6 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, } adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); - adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { @@ -1538,23 +1537,30 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, while (left_len > sizeof(struct mwifiex_ie_types_header)) { tlv = (void *)&hw_spec->tlvs + parsed_len; switch (le16_to_cpu(tlv->type)) { - case TLV_TYPE_FW_API_REV: - api_rev = (struct hw_spec_fw_api_rev *)tlv; + case TLV_TYPE_API_REV: + api_rev = (struct hw_spec_api_rev *)tlv; api_id = le16_to_cpu(api_rev->api_id); switch (api_id) { case KEY_API_VER_ID: - adapter->fw_key_api_major_ver = + adapter->key_api_major_ver = api_rev->major_ver; - adapter->fw_key_api_minor_ver = + adapter->key_api_minor_ver = api_rev->minor_ver; dev_dbg(adapter->dev, - "fw_key_api v%d.%d\n", - adapter->fw_key_api_major_ver, - adapter->fw_key_api_minor_ver); + "key_api v%d.%d\n", + adapter->key_api_major_ver, + adapter->key_api_minor_ver); + break; + case FW_API_VER_ID: + adapter->fw_api_ver = + api_rev->major_ver; + dev_dbg(adapter->dev, + "Firmware api version %d\n", + adapter->fw_api_ver); break; default: dev_warn(adapter->dev, - "Unknown FW api_id: %d\n", + "Unknown api_id: %d\n", api_id); break; } @@ -1567,7 +1573,8 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, } parsed_len += le16_to_cpu(tlv->len) + sizeof(struct mwifiex_ie_types_header); - left_len -= parsed_len; + left_len -= le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); } } diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 49da2d53d294..6a703ea18800 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -83,7 +83,7 @@ enum KEY_TYPE_ID { #define WPA_PN_SIZE 8 #define KEY_PARAMS_FIXED_LEN 10 #define KEY_INDEX_MASK 0xf -#define FW_KEY_API_VER_MAJOR_V2 2 +#define KEY_API_VER_MAJOR_V2 2 #define KEY_MCAST BIT(0) #define KEY_UNICAST BIT(1) @@ -170,7 +170,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) #define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) #define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) -#define TLV_TYPE_FW_API_REV (PROPRIETARY_TLV_BASE_ID + 199) +#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 @@ -844,11 +844,12 @@ struct host_cmd_ds_802_11_ps_mode_enh { } params; } __packed; -enum FW_API_VER_ID { +enum API_VER_ID { KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, }; -struct hw_spec_fw_api_rev { +struct hw_spec_api_rev { struct mwifiex_ie_types_header header; __le16 api_id; u8 major_ver; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 269a277d0a2e..80bda8087508 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -282,8 +282,8 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; adapter->empty_tx_q_cnt = 0; adapter->ext_scan = true; - adapter->fw_key_api_major_ver = 0; - adapter->fw_key_api_minor_ver = 0; + adapter->key_api_major_ver = 0; + adapter->key_api_minor_ver = 0; } /* diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index a2733b1e63f9..54399631599a 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -833,7 +833,7 @@ struct mwifiex_adapter { struct semaphore *card_sem; bool ext_scan; u8 fw_api_ver; - u8 fw_key_api_major_ver, fw_key_api_minor_ver; + u8 key_api_major_ver, key_api_minor_ver; struct work_struct iface_work; unsigned long iface_work_flags; struct memory_type_mapping *mem_type_mapping_tbl; diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index dee717a19ddb..195ef0ca343f 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1719,7 +1719,8 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { bss = cfg80211_inform_bss(priv->wdev->wiphy, - chan, bssid, timestamp, + chan, CFG80211_BSS_FTYPE_UNKNOWN, + bssid, timestamp, cap_info_bitmap, beacon_period, ie_buf, ie_len, rssi, GFP_KERNEL); bss_priv = (struct mwifiex_bss_priv *)bss->priv; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 733de92a4c61..225f7498048b 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -965,7 +965,7 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, u16 cmd_action, u32 cmd_oid, struct mwifiex_ds_encrypt_key *enc_key) { - if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) return mwifiex_cmd_802_11_key_material_v2(priv, cmd, cmd_action, cmd_oid, enc_key); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 08b78baeb846..62866b0f0830 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -637,7 +637,7 @@ static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { - if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) return mwifiex_ret_802_11_key_material_v2(priv, resp); else return mwifiex_ret_802_11_key_material_v1(priv, resp); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index caae9738100a..b95a29b868d1 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -877,7 +877,7 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, return -1; } - if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) { + if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) { memcpy(encrypt_key->key_material, wep_key->key_material, wep_key->key_length); encrypt_key->key_len = wep_key->key_length; @@ -903,7 +903,7 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, memset(&priv->wep_key[index], 0, sizeof(struct mwifiex_wep_key)); - if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) enc_key = encrypt_key; else enc_key = NULL; diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index cee028321a9a..ec79c49de097 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -172,7 +172,7 @@ mwifiex_process_mgmt_packet(struct mwifiex_private *priv, cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq, CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, - 0, GFP_ATOMIC); + 0); return 0; } diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c index d3cf7c3ebfd6..f4b784fc882b 100644 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -1605,10 +1605,7 @@ static int ezusb_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->bNumEndpoints; ++i) { ep = &interface->altsetting[0].endpoint[i].desc; - if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN) && - ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { + if (usb_endpoint_is_bulk_in(ep)) { /* we found a bulk in endpoint */ if (upriv->read_urb != NULL) { pr_warning("Found a second bulk in ep, ignored"); @@ -1636,10 +1633,7 @@ static int ezusb_probe(struct usb_interface *interface, } } - if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_OUT) && - ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { + if (usb_endpoint_is_bulk_out(ep)) { /* we found a bulk out endpoint */ if (upriv->bap_buf != NULL) { pr_warning("Found a second bulk out ep, ignored"); diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index e175b9b8561b..2c66166add70 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -123,9 +123,10 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv, beacon_interval = le16_to_cpu(bss->a.beacon_interv); signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); - cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, - capability, beacon_interval, ie_buf, ie_len, - signal, GFP_KERNEL); + cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, + bss->a.bssid, timestamp, capability, + beacon_interval, ie_buf, ie_len, signal, + GFP_KERNEL); cfg80211_put_bss(wiphy, cbss); } @@ -156,9 +157,10 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, ie = bss->data; signal = SIGNAL_TO_MBM(bss->level); - cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, - capability, beacon_interval, ie, ie_len, - signal, GFP_KERNEL); + cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, + bss->bssid, timestamp, capability, + beacon_interval, ie, ie_len, signal, + GFP_KERNEL); cfg80211_put_bss(wiphy, cbss); } diff --git a/drivers/net/wireless/ray_cs.h b/drivers/net/wireless/ray_cs.h index e79848fbcca1..524c2f02dd82 100644 --- a/drivers/net/wireless/ray_cs.h +++ b/drivers/net/wireless/ray_cs.h @@ -3,7 +3,8 @@ Written by Corey Thomas */ -#ifndef RAYLINK_H +#ifndef _RAY_CS_H_ +#define _RAY_CS_H_ struct beacon_rx { struct mac_header mac; @@ -69,4 +70,4 @@ typedef struct ray_dev_t { } ray_dev_t; /*****************************************************************************/ -#endif /* RAYLINK_H */ +#endif /* _RAY_CS_H_ */ diff --git a/drivers/net/wireless/rayctl.h b/drivers/net/wireless/rayctl.h index 3c3b98b152c3..b21ed64e15df 100644 --- a/drivers/net/wireless/rayctl.h +++ b/drivers/net/wireless/rayctl.h @@ -1,4 +1,5 @@ -#ifndef RAYLINK_H +#ifndef _RAYCTL_H_ +#define _RAYCTL_H_ typedef unsigned char UCHAR; @@ -729,4 +730,4 @@ typedef struct snaphdr_t #define RAY_IPX_TYPE 0x8137 #define APPLEARP_TYPE 0x80f3 /*****************************************************************************/ -#endif /* #ifndef RAYLINK_H */ +#endif /* _RAYCTL_H_ */ diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index d2a9a08210be..1a4facd1fbf3 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2022,9 +2022,10 @@ static bool rndis_bss_info_update(struct usbnet *usbdev, capability = le16_to_cpu(fixed->capabilities); beacon_interval = le16_to_cpu(fixed->beacon_interval); - bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac, - timestamp, capability, beacon_interval, ie, ie_len, signal, - GFP_KERNEL); + bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid->mac, + timestamp, capability, beacon_interval, + ie, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(priv->wdev.wiphy, bss); return (bss != NULL); @@ -2711,9 +2712,10 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, bssid, (u32)timestamp, capability, beacon_period, ie_len, ssid.essid, signal); - bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid, - timestamp, capability, beacon_period, ie_buf, ie_len, - signal, GFP_KERNEL); + bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, + timestamp, capability, beacon_period, + ie_buf, ie_len, signal, GFP_KERNEL); cfg80211_put_bss(priv->wdev.wiphy, bss); } diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index a394a9a95919..b7434df51e7c 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -52,6 +52,7 @@ * RF5592 2.4G/5G 2T2R * RF3070 2.4G 1T1R * RF5360 2.4G 1T1R + * RF5362 2.4G 1T1R * RF5370 2.4G 1T1R * RF5390 2.4G 1T1R */ @@ -72,6 +73,7 @@ #define RF3070 0x3070 #define RF3290 0x3290 #define RF5360 0x5360 +#define RF5362 0x5362 #define RF5370 0x5370 #define RF5372 0x5372 #define RF5390 0x5390 @@ -2145,7 +2147,7 @@ struct mac_iveiv_entry { /* Bits [7-4] for RF3320 (RT3370/RT3390), on other chipsets reserved */ #define RFCSR3_PA1_BIAS_CCK FIELD8(0x70) #define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80) -/* Bits for RF3290/RF5360/RF5370/RF5372/RF5390/RF5392 */ +/* Bits for RF3290/RF5360/RF5362/RF5370/RF5372/RF5390/RF5392 */ #define RFCSR3_VCOCAL_EN FIELD8(0x80) /* Bits for RF3050 */ #define RFCSR3_BIT1 FIELD8(0x02) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 893c9d5f3d6f..9f57a2db791c 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3186,6 +3186,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, break; case RF3070: case RF5360: + case RF5362: case RF5370: case RF5372: case RF5390: @@ -3203,6 +3204,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2x00_rf(rt2x00dev, RF3290) || rt2x00_rf(rt2x00dev, RF3322) || rt2x00_rf(rt2x00dev, RF5360) || + rt2x00_rf(rt2x00dev, RF5362) || rt2x00_rf(rt2x00dev, RF5370) || rt2x00_rf(rt2x00dev, RF5372) || rt2x00_rf(rt2x00dev, RF5390) || @@ -4317,6 +4319,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) case RF3070: case RF3290: case RF5360: + case RF5362: case RF5370: case RF5372: case RF5390: @@ -7095,6 +7098,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF3320: case RF3322: case RF5360: + case RF5362: case RF5370: case RF5372: case RF5390: @@ -7551,6 +7555,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3320: case RF3322: case RF5360: + case RF5362: case RF5370: case RF5372: case RF5390: @@ -7680,6 +7685,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3070: case RF3290: case RF5360: + case RF5362: case RF5370: case RF5372: case RF5390: diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index a0aa8fa72392..735be5352143 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -345,7 +345,6 @@ static int wl1251_spi_remove(struct spi_device *spi) { struct wl1251 *wl = spi_get_drvdata(spi); - free_irq(wl->irq, wl); wl1251_free_hw(wl); regulator_disable(wl->vio); diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 392c882b28f0..69601f6741d9 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -327,23 +327,22 @@ static int wl1271_probe(struct spi_device *spi) struct wl12xx_spi_glue *glue; struct wlcore_platdev_data pdev_data; struct resource res[1]; - int ret = -ENOMEM; + int ret; memset(&pdev_data, 0x00, sizeof(pdev_data)); pdev_data.pdata = dev_get_platdata(&spi->dev); if (!pdev_data.pdata) { dev_err(&spi->dev, "no platform data\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } pdev_data.if_ops = &spi_ops; - glue = kzalloc(sizeof(*glue), GFP_KERNEL); + glue = devm_kzalloc(&spi->dev, sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&spi->dev, "can't allocate glue\n"); - goto out; + return -ENOMEM; } glue->dev = &spi->dev; @@ -357,14 +356,13 @@ static int wl1271_probe(struct spi_device *spi) ret = spi_setup(spi); if (ret < 0) { dev_err(glue->dev, "spi_setup failed\n"); - goto out_free_glue; + return ret; } glue->core = platform_device_alloc("wl12xx", PLATFORM_DEVID_AUTO); if (!glue->core) { dev_err(glue->dev, "can't allocate platform_device\n"); - ret = -ENOMEM; - goto out_free_glue; + return -ENOMEM; } glue->core->dev.parent = &spi->dev; @@ -398,11 +396,6 @@ static int wl1271_probe(struct spi_device *spi) out_dev_put: platform_device_put(glue->core); - -out_free_glue: - kfree(glue); - -out: return ret; } @@ -411,7 +404,6 @@ static int wl1271_remove(struct spi_device *spi) struct wl12xx_spi_glue *glue = spi_get_drvdata(spi); platform_device_unregister(glue->core); - kfree(glue); return 0; } diff --git a/drivers/staging/rtl8723au/core/rtw_mlme_ext.c b/drivers/staging/rtl8723au/core/rtw_mlme_ext.c index c5fdcb89dacd..2a1502f351f8 100644 --- a/drivers/staging/rtl8723au/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723au/core/rtw_mlme_ext.c @@ -2128,7 +2128,7 @@ static int on_action_public23a(struct rtw_adapter *padapter, IEEE80211_BAND_5GHZ); if (cfg80211_rx_mgmt(padapter->rtw_wdev, freq, 0, pframe, - skb->len, 0, GFP_ATOMIC)) + skb->len, 0)) return _SUCCESS; return _FAIL; diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c index 93dc844a10b3..8b0ccb5c5fc4 100644 --- a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c @@ -279,6 +279,7 @@ static int rtw_cfg80211_inform_bss(struct rtw_adapter *padapter, } bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, pnetwork->network.MacAddress, pnetwork->network.tsf, pnetwork->network.capability, @@ -2379,7 +2380,7 @@ void rtw_cfg80211_indicate_sta_assoc(struct rtw_adapter *padapter, IEEE80211_BAND_5GHZ); cfg80211_rx_mgmt(padapter->rtw_wdev, freq, 0, pmgmt_frame, frame_len, - 0, GFP_ATOMIC); + 0); #endif /* defined(RTW_USE_CFG80211_STA_EVENT) */ } @@ -2425,7 +2426,7 @@ void rtw_cfg80211_indicate_sta_disassoc(struct rtw_adapter *padapter, frame_len = sizeof(struct ieee80211_hdr_3addr) + 2; cfg80211_rx_mgmt(padapter->rtw_wdev, freq, 0, (u8 *)&mgmt, frame_len, - 0, GFP_ATOMIC); + 0); #endif /* defined(RTW_USE_CFG80211_STA_EVENT) */ } diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 3727f6d25cf1..8942dcb44180 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -422,6 +422,7 @@ static int prism2_scan(struct wiphy *wiphy, IEEE80211_BAND_2GHZ); bss = cfg80211_inform_bss(wiphy, ieee80211_get_channel(wiphy, freq), + CFG80211_BSS_FTYPE_UNKNOWN, (const u8 *) &(msg2.bssid.data.data), msg2.timestamp.data, msg2.capinfo.data, msg2.beaconperiod.data, diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h index 917dcd7965e7..e64ae7bf80a1 100644 --- a/include/linux/bcma/bcma_regs.h +++ b/include/linux/bcma/bcma_regs.h @@ -39,6 +39,11 @@ #define BCMA_RESET_CTL_RESET 0x0001 #define BCMA_RESET_ST 0x0804 +#define BCMA_NS_ROM_IOST_BOOT_DEV_MASK 0x0003 +#define BCMA_NS_ROM_IOST_BOOT_DEV_NOR 0x0000 +#define BCMA_NS_ROM_IOST_BOOT_DEV_NAND 0x0001 +#define BCMA_NS_ROM_IOST_BOOT_DEV_ROM 0x0002 + /* BCMA PCI config space registers. */ #define BCMA_PCI_PMCSR 0x44 #define BCMA_PCI_PE 0x100 diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 63ab3873c5ed..8018c915ee63 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -838,6 +838,16 @@ enum ieee80211_vht_opmode_bits { #define WLAN_SA_QUERY_TR_ID_LEN 2 +/** + * struct ieee80211_tpc_report_ie + * + * This structure refers to "TPC Report element" + */ +struct ieee80211_tpc_report_ie { + u8 tx_power; + u8 link_margin; +} __packed; + struct ieee80211_mgmt { __le16 frame_control; __le16 duration; @@ -973,6 +983,13 @@ struct ieee80211_mgmt { u8 action_code; u8 operating_mode; } __packed vht_opmode_notif; + struct { + u8 action_code; + u8 dialog_token; + u8 tpc_elem_id; + u8 tpc_elem_length; + struct ieee80211_tpc_report_ie tpc; + } __packed tpc_report; } u; } __packed action; } u; @@ -1865,6 +1882,7 @@ enum ieee80211_category { WLAN_CATEGORY_DLS = 2, WLAN_CATEGORY_BACK = 3, WLAN_CATEGORY_PUBLIC = 4, + WLAN_CATEGORY_RADIO_MEASUREMENT = 5, WLAN_CATEGORY_HT = 7, WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9, @@ -2378,4 +2396,51 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, #define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) #define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) +/** + * ieee80211_action_contains_tpc - checks if the frame contains TPC element + * @skb: the skb containing the frame, length will be checked + * + * This function checks if it's either TPC report action frame or Link + * Measurement report action frame as defined in IEEE Std. 802.11-2012 8.5.2.5 + * and 8.5.7.5 accordingly. + */ +static inline bool ieee80211_action_contains_tpc(struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + if (!ieee80211_is_action(mgmt->frame_control)) + return false; + + if (skb->len < IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.tpc_report)) + return false; + + /* + * TPC report - check that: + * category = 0 (Spectrum Management) or 5 (Radio Measurement) + * spectrum management action = 3 (TPC/Link Measurement report) + * TPC report EID = 35 + * TPC report element length = 2 + * + * The spectrum management's tpc_report struct is used here both for + * parsing tpc_report and radio measurement's link measurement report + * frame, since the relevant part is identical in both frames. + */ + if (mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT && + mgmt->u.action.category != WLAN_CATEGORY_RADIO_MEASUREMENT) + return false; + + /* both spectrum mgmt and link measurement have same action code */ + if (mgmt->u.action.u.tpc_report.action_code != + WLAN_ACTION_SPCT_TPC_RPRT) + return false; + + if (mgmt->u.action.u.tpc_report.tpc_elem_id != WLAN_EID_TPC_REPORT || + mgmt->u.action.u.tpc_report.tpc_elem_length != + sizeof(struct ieee80211_tpc_report_ie)) + return false; + + return true; +} + #endif /* LINUX_IEEE80211_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6f884e6c731e..b0ded1333865 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -302,7 +302,7 @@ struct hci_dev { __u32 req_status; __u32 req_result; - struct crypto_blkcipher *tfm_aes; + void *smp_data; struct discovery_state discovery; struct hci_conn_hash conn_hash; @@ -970,6 +970,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE)) #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR)) +#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ + !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 @@ -1258,6 +1261,8 @@ bool hci_req_pending(struct hci_dev *hdev); void hci_req_add_le_scan_disable(struct hci_request *req); void hci_req_add_le_passive_scan(struct hci_request *req); +void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req); + struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, @@ -1353,6 +1358,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); void mgmt_discovering(struct hci_dev *hdev, u8 discovering); +bool mgmt_powering_down(struct hci_dev *hdev); void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 8df15ad0d43f..cedda399f9c0 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -625,6 +625,9 @@ struct l2cap_conn { struct delayed_work info_timer; + int disconn_err; + struct work_struct disconn_work; + struct sk_buff *rx_skb; __u32 rx_len; __u8 tx_ident; @@ -635,8 +638,7 @@ struct l2cap_conn { __u8 disc_reason; - struct delayed_work security_timer; - struct smp_chan *smp_chan; + struct l2cap_chan *smp; struct list_head chan_l; struct mutex chan_lock; @@ -708,6 +710,7 @@ enum { FLAG_EFS_ENABLE, FLAG_DEFER_SETUP, FLAG_LE_CONN_REQ_SENT, + FLAG_PENDING_SECURITY, }; enum { @@ -837,18 +840,43 @@ static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan return NULL; } +static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb) +{ + return -ENOSYS; +} + +static inline struct sk_buff *l2cap_chan_no_alloc_skb(struct l2cap_chan *chan, + unsigned long hdr_len, + unsigned long len, int nb) +{ + return ERR_PTR(-ENOSYS); +} + static inline void l2cap_chan_no_teardown(struct l2cap_chan *chan, int err) { } +static inline void l2cap_chan_no_close(struct l2cap_chan *chan) +{ +} + static inline void l2cap_chan_no_ready(struct l2cap_chan *chan) { } +static inline void l2cap_chan_no_state_change(struct l2cap_chan *chan, + int state, int err) +{ +} + static inline void l2cap_chan_no_defer(struct l2cap_chan *chan) { } +static inline void l2cap_chan_no_suspend(struct l2cap_chan *chan) +{ +} + static inline void l2cap_chan_no_resume(struct l2cap_chan *chan) { } @@ -918,6 +946,7 @@ void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, u8 status); void __l2cap_physical_cfm(struct l2cap_chan *chan, int result); +void l2cap_conn_shutdown(struct l2cap_conn *conn, int err); void l2cap_conn_get(struct l2cap_conn *conn); void l2cap_conn_put(struct l2cap_conn *conn); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0a080c4de275..ab21299c8f4d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1503,12 +1503,14 @@ enum cfg80211_signal_type { * @tsf: TSF contained in the frame that carried these IEs * @rcu_head: internal use, for freeing * @len: length of the IEs + * @from_beacon: these IEs are known to come from a beacon * @data: IE data */ struct cfg80211_bss_ies { u64 tsf; struct rcu_head rcu_head; int len; + bool from_beacon; u8 data[]; }; @@ -3765,11 +3767,25 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, } /** - * cfg80211_inform_bss - inform cfg80211 of a new BSS + * enum cfg80211_bss_frame_type - frame type that the BSS data came from + * @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is + * from a beacon or probe response + * @CFG80211_BSS_FTYPE_BEACON: data comes from a beacon + * @CFG80211_BSS_FTYPE_PRESP: data comes from a probe response + */ +enum cfg80211_bss_frame_type { + CFG80211_BSS_FTYPE_UNKNOWN, + CFG80211_BSS_FTYPE_BEACON, + CFG80211_BSS_FTYPE_PRESP, +}; + +/** + * cfg80211_inform_bss_width - inform cfg80211 of a new BSS * * @wiphy: the wiphy reporting the BSS * @rx_channel: The channel the frame was received on * @scan_width: width of the control channel + * @ftype: frame type (if known) * @bssid: the BSSID of the BSS * @tsf: the TSF sent by the peer in the beacon/probe response (or 0) * @capability: the capability field sent by the peer @@ -3789,6 +3805,7 @@ struct cfg80211_bss * __must_check cfg80211_inform_bss_width(struct wiphy *wiphy, struct ieee80211_channel *rx_channel, enum nl80211_bss_scan_width scan_width, + enum cfg80211_bss_frame_type ftype, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp); @@ -3796,12 +3813,13 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, static inline struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *rx_channel, + enum cfg80211_bss_frame_type ftype, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) { return cfg80211_inform_bss_width(wiphy, rx_channel, - NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_20, ftype, bssid, tsf, capability, beacon_interval, ie, ielen, signal, gfp); @@ -4412,7 +4430,6 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * @buf: Management frame (header + body) * @len: length of the frame data * @flags: flags, as defined in enum nl80211_rxmgmt_flags - * @gfp: context flags * * This function is called whenever an Action frame is received for a station * mode interface, but is not processed in kernel. @@ -4423,7 +4440,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * driver is responsible for rejecting the frame. */ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm, - const u8 *buf, size_t len, u32 flags, gfp_t gfp); + const u8 *buf, size_t len, u32 flags); /** * cfg80211_mgmt_tx_status - notification of TX status for management frame diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dae2e24616e1..c9b2bec8db47 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1226,7 +1226,8 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); * * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the * driver to indicate that it requires IV generation for this - * particular key. + * particular key. Setting this flag does not necessarily mean that SKBs + * will have sufficient tailroom for ICV or MIC. * @IEEE80211_KEY_FLAG_GENERATE_MMIC: This flag should be set by * the driver for a TKIP key if it requires Michael MIC * generation in software. @@ -1238,7 +1239,9 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver * if space should be prepared for the IV, but the IV * itself should not be generated. Do not set together with - * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. + * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. Setting this flag does + * not necessarily mean that SKBs will have sufficient tailroom for ICV or + * MIC. * @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received * management frames. The flag can help drivers that have a hardware * crypto implementation that doesn't deal with management frames @@ -1405,7 +1408,7 @@ struct ieee80211_sta_rates { * @supp_rates: Bitmap of supported rates (per band) * @ht_cap: HT capabilities of this STA; restricted to our own capabilities * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities - * @wme: indicates whether the STA supports WME. Only valid during AP-mode. + * @wme: indicates whether the STA supports QoS/WME. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. * @uapsd_queues: bitmap of queues configured for uapsd. Only valid @@ -1606,6 +1609,9 @@ struct ieee80211_tx_control { * is not enabled the default action is to disconnect when getting the * CSA frame. * + * @IEEE80211_HW_SUPPORTS_CLONED_SKBS: The driver will never modify the payload + * or tailroom of TX skbs without copying them first. + * * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands * in one command, mac80211 doesn't have to run separate scans per band. */ @@ -1639,7 +1645,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, - /* bit 29 unused */ + IEEE80211_HW_SUPPORTS_CLONED_SKBS = 1<<29, IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30, }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index f1db15b9c041..d097568da690 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3055,14 +3055,20 @@ enum nl80211_bss_scan_width { * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * (if @NL80211_BSS_PRESP_DATA is present then this is known to be + * from a probe response, otherwise it may be from the same beacon + * that the NL80211_BSS_BEACON_TSF will be from) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the * raw information elements from the probe response/beacon (bin); - * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are - * from a Probe Response frame; otherwise they are from a Beacon frame. + * if the %NL80211_BSS_BEACON_IES attribute is present and the data is + * different then the IEs here are from a Probe Response frame; otherwise + * they are from a Beacon frame. * However, if the driver does not indicate the source of the IEs, these * IEs may be from either frame subtype. + * If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the + * data here is known to be from a probe response, without any heuristics. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon @@ -3074,6 +3080,10 @@ enum nl80211_bss_scan_width { * yet been received * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel * (u32, enum nl80211_bss_scan_width) + * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64) + * (not present if no beacon frame has been received yet) + * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and + * @NL80211_BSS_TSF is known to be from a probe response (flag attribute) * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -3091,6 +3101,8 @@ enum nl80211_bss { NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, NL80211_BSS_CHAN_WIDTH, + NL80211_BSS_BEACON_TSF, + NL80211_BSS_PRESP_DATA, /* keep last */ __NL80211_BSS_AFTER_LAST, diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 206b65ccd5b8..35ebe79c87b0 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -772,16 +772,16 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) ifup(dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *chan) +static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan) { - struct l2cap_chan *pchan; + struct l2cap_chan *chan; - pchan = chan_open(chan); - pchan->ops = chan->ops; + chan = chan_open(pchan); + chan->ops = pchan->ops; BT_DBG("chan %p pchan %p", chan, pchan); - return pchan; + return chan; } static void delete_netdev(struct work_struct *work) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1d9c29a00568..9b7145959a49 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1898,6 +1898,8 @@ static int __hci_init(struct hci_dev *hdev) debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, &hdev->discov_interleaved_timeout); + + smp_register(hdev); } return 0; @@ -3238,7 +3240,7 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa) } list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { - if (smp_irk_matches(hdev->tfm_aes, irk->val, rpa)) { + if (smp_irk_matches(hdev, irk->val, rpa)) { bacpy(&irk->rpa, rpa); return irk; } @@ -3892,7 +3894,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, !bacmp(&hdev->random_addr, &hdev->rpa)) return 0; - err = smp_generate_rpa(hdev->tfm_aes, hdev->irk, &hdev->rpa); + err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa); if (err < 0) { BT_ERR("%s failed to generate new RPA", hdev->name); return err; @@ -4100,18 +4102,9 @@ int hci_register_dev(struct hci_dev *hdev) dev_set_name(&hdev->dev, "%s", hdev->name); - hdev->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(hdev->tfm_aes)) { - BT_ERR("Unable to create crypto context"); - error = PTR_ERR(hdev->tfm_aes); - hdev->tfm_aes = NULL; - goto err_wqueue; - } - error = device_add(&hdev->dev); if (error < 0) - goto err_tfm; + goto err_wqueue; hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, @@ -4153,8 +4146,6 @@ int hci_register_dev(struct hci_dev *hdev) return id; -err_tfm: - crypto_free_blkcipher(hdev->tfm_aes); err_wqueue: destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->req_workqueue); @@ -4206,8 +4197,7 @@ void hci_unregister_dev(struct hci_dev *hdev) rfkill_destroy(hdev->rfkill); } - if (hdev->tfm_aes) - crypto_free_blkcipher(hdev->tfm_aes); + smp_unregister(hdev); device_del(&hdev->dev); @@ -5690,3 +5680,52 @@ void hci_update_background_scan(struct hci_dev *hdev) if (err) BT_ERR("Failed to run HCI request: err %d", err); } + +static bool disconnected_whitelist_entries(struct hci_dev *hdev) +{ + struct bdaddr_list *b; + + list_for_each_entry(b, &hdev->whitelist, list) { + struct hci_conn *conn; + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr); + if (!conn) + return true; + + if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) + return true; + } + + return false; +} + +void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req) +{ + u8 scan; + + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return; + + if (!hdev_is_powered(hdev)) + return; + + if (mgmt_powering_down(hdev)) + return; + + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || + disconnected_whitelist_entries(hdev)) + scan = SCAN_PAGE; + else + scan = SCAN_DISABLED; + + if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE)) + return; + + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + if (req) + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + else + hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a6000823f0ff..3a99f30a3317 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2071,6 +2071,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) cp.handle = ev->handle; hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp); + + hci_update_page_scan(hdev, NULL); } /* Set packet type for incoming connection */ @@ -2247,9 +2249,12 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type, reason, mgmt_connected); - if (conn->type == ACL_LINK && - test_bit(HCI_CONN_FLUSH_KEY, &conn->flags)) - hci_remove_link_key(hdev, &conn->dst); + if (conn->type == ACL_LINK) { + if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags)) + hci_remove_link_key(hdev, &conn->dst); + + hci_update_page_scan(hdev, NULL); + } params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 46547b920f88..4a90438d99df 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -210,6 +210,10 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) { write_lock(&chan_list_lock); + /* Override the defaults (which are for conn-oriented) */ + chan->omtu = L2CAP_DEFAULT_MTU; + chan->chan_type = L2CAP_CHAN_FIXED; + chan->scid = scid; write_unlock(&chan_list_lock); @@ -562,6 +566,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) BT_DBG("chan %p, conn %p, err %d", chan, conn, err); + chan->ops->teardown(chan, err); + if (conn) { struct amp_mgr *mgr = conn->hcon->amp_mgr; /* Delete from channel list */ @@ -585,8 +591,6 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) amp_disconnect_logical_link(hs_hchan); } - chan->ops->teardown(chan, err); - if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) return; @@ -1082,6 +1086,9 @@ static void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, bool poll) static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) + return true; + return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } @@ -1417,71 +1424,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn) mutex_unlock(&conn->chan_lock); } -/* Find socket with cid and source/destination bdaddr. - * Returns closest match, locked. - */ -static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, - bdaddr_t *src, - bdaddr_t *dst) -{ - struct l2cap_chan *c, *c1 = NULL; - - read_lock(&chan_list_lock); - - list_for_each_entry(c, &chan_list, global_l) { - if (state && c->state != state) - continue; - - if (c->scid == cid) { - int src_match, dst_match; - int src_any, dst_any; - - /* Exact match. */ - src_match = !bacmp(&c->src, src); - dst_match = !bacmp(&c->dst, dst); - if (src_match && dst_match) { - read_unlock(&chan_list_lock); - return c; - } - - /* Closest match */ - src_any = !bacmp(&c->src, BDADDR_ANY); - dst_any = !bacmp(&c->dst, BDADDR_ANY); - if ((src_match && dst_any) || (src_any && dst_match) || - (src_any && dst_any)) - c1 = c; - } - } - - read_unlock(&chan_list_lock); - - return c1; -} - static void l2cap_le_conn_ready(struct l2cap_conn *conn) { struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; - struct l2cap_chan *chan, *pchan; - u8 dst_type; - BT_DBG(""); + BT_DBG("%s conn %p", hdev->name, conn); - /* Check if we have socket listening on cid */ - pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT, - &hcon->src, &hcon->dst); - if (!pchan) - return; - - /* Client ATT sockets should override the server one */ - if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT)) - return; - - dst_type = bdaddr_type(hcon, hcon->dst_type); - - /* If device is blocked, do not create a channel for it */ - if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type)) - return; + /* For outgoing pairing which doesn't necessarily have an + * associated socket (e.g. mgmt_pair_device). + */ + if (hcon->out) + smp_conn_security(hcon, hcon->pending_sec_level); /* For LE slave connections, make sure the connection interval * is in the range of the minium and maximum interval that has @@ -1501,22 +1455,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req); } - - l2cap_chan_lock(pchan); - - chan = pchan->ops->new_connection(pchan); - if (!chan) - goto clean; - - bacpy(&chan->src, &hcon->src); - bacpy(&chan->dst, &hcon->dst); - chan->src_type = bdaddr_type(hcon, hcon->src_type); - chan->dst_type = dst_type; - - __l2cap_chan_add(conn, chan); - -clean: - l2cap_chan_unlock(pchan); } static void l2cap_conn_ready(struct l2cap_conn *conn) @@ -1526,17 +1464,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) BT_DBG("conn %p", conn); - /* For outgoing pairing which doesn't necessarily have an - * associated socket (e.g. mgmt_pair_device). - */ - if (hcon->out && hcon->type == LE_LINK) - smp_conn_security(hcon, hcon->pending_sec_level); - mutex_lock(&conn->chan_lock); - if (hcon->type == LE_LINK) - l2cap_le_conn_ready(conn); - list_for_each_entry(chan, &conn->chan_l, list) { l2cap_chan_lock(chan); @@ -1560,6 +1489,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) mutex_unlock(&conn->chan_lock); + if (hcon->type == LE_LINK) + l2cap_le_conn_ready(conn); + queue_work(hcon->hdev->workqueue, &conn->pending_rx_work); } @@ -1695,6 +1627,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (work_pending(&conn->pending_rx_work)) cancel_work_sync(&conn->pending_rx_work); + if (work_pending(&conn->disconn_work)) + cancel_work_sync(&conn->disconn_work); + l2cap_unregister_all_users(conn); mutex_lock(&conn->chan_lock); @@ -1719,27 +1654,29 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) cancel_delayed_work_sync(&conn->info_timer); - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) { - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } - hcon->l2cap_data = NULL; conn->hchan = NULL; l2cap_conn_put(conn); } -static void security_timeout(struct work_struct *work) +static void disconn_work(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - security_timer.work); + disconn_work); BT_DBG("conn %p", conn); - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { - smp_chan_destroy(conn); - l2cap_conn_del(conn->hcon, ETIMEDOUT); - } + l2cap_conn_del(conn->hcon, conn->disconn_err); +} + +void l2cap_conn_shutdown(struct l2cap_conn *conn, int err) +{ + struct hci_dev *hdev = conn->hcon->hdev; + + BT_DBG("conn %p err %d", conn, err); + + conn->disconn_err = err; + queue_work(hdev->workqueue, &conn->disconn_work); } static void l2cap_conn_free(struct kref *ref) @@ -1794,6 +1731,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, src_match = !bacmp(&c->src, src); dst_match = !bacmp(&c->dst, dst); if (src_match && dst_match) { + l2cap_chan_hold(c); read_unlock(&chan_list_lock); return c; } @@ -1807,6 +1745,9 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, } } + if (c1) + l2cap_chan_hold(c1); + read_unlock(&chan_list_lock); return c1; @@ -2027,10 +1968,12 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) tx_skb->data + L2CAP_HDR_SIZE); } + /* Update FCS */ if (chan->fcs == L2CAP_FCS_CRC16) { - u16 fcs = crc16(0, (u8 *) tx_skb->data, tx_skb->len); - put_unaligned_le16(fcs, skb_put(tx_skb, - L2CAP_FCS_SIZE)); + u16 fcs = crc16(0, (u8 *) tx_skb->data, + tx_skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, skb_tail_pointer(tx_skb) - + L2CAP_FCS_SIZE); } l2cap_do_send(chan, tx_skb); @@ -2334,7 +2277,6 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, } else { sar = L2CAP_SAR_START; sdu_len = len; - pdu_len -= L2CAP_SDULEN_SIZE; } while (len > 0) { @@ -2349,10 +2291,8 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, __skb_queue_tail(seg_queue, skb); len -= pdu_len; - if (sdu_len) { + if (sdu_len) sdu_len = 0; - pdu_len += L2CAP_SDULEN_SIZE; - } if (len <= pdu_len) { sar = L2CAP_SAR_END; @@ -3884,6 +3824,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, response: l2cap_chan_unlock(pchan); mutex_unlock(&conn->chan_lock); + l2cap_chan_put(pchan); sendresp: rsp.scid = cpu_to_le16(scid); @@ -5497,6 +5438,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, response_unlock: l2cap_chan_unlock(pchan); mutex_unlock(&conn->chan_lock); + l2cap_chan_put(pchan); if (result == L2CAP_CR_PEND) return 0; @@ -6845,12 +6787,12 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct l2cap_chan *chan; if (hcon->type != ACL_LINK) - goto drop; + goto free_skb; chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst, ACL_LINK); if (!chan) - goto drop; + goto free_skb; BT_DBG("chan %p, len %d", chan, skb->len); @@ -6864,36 +6806,14 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, bacpy(&bt_cb(skb)->bdaddr, &hcon->dst); bt_cb(skb)->psm = psm; - if (!chan->ops->recv(chan, skb)) - return; - -drop: - kfree_skb(skb); -} - -static void l2cap_att_channel(struct l2cap_conn *conn, - struct sk_buff *skb) -{ - struct hci_conn *hcon = conn->hcon; - struct l2cap_chan *chan; - - if (hcon->type != LE_LINK) - goto drop; - - chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT, - &hcon->src, &hcon->dst); - if (!chan) - goto drop; - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan, skb)) + if (!chan->ops->recv(chan, skb)) { + l2cap_chan_put(chan); return; + } drop: + l2cap_chan_put(chan); +free_skb: kfree_skb(skb); } @@ -6942,19 +6862,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_conless_channel(conn, psm, skb); break; - case L2CAP_CID_ATT: - l2cap_att_channel(conn, skb); - break; - case L2CAP_CID_LE_SIGNALING: l2cap_le_sig_channel(conn, skb); break; - case L2CAP_CID_SMP: - if (smp_sig_channel(conn, skb)) - l2cap_conn_del(conn->hcon, EACCES); - break; - default: l2cap_data_channel(conn, cid, skb); break; @@ -7023,10 +6934,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) INIT_LIST_HEAD(&conn->chan_l); INIT_LIST_HEAD(&conn->users); - if (hcon->type == LE_LINK) - INIT_DELAYED_WORK(&conn->security_timer, security_timeout); - else - INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); + INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); + + INIT_WORK(&conn->disconn_work, disconn_work); skb_queue_head_init(&conn->pending_rx); INIT_WORK(&conn->pending_rx_work, process_pending_rx); @@ -7239,19 +7149,99 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) return exact ? lm1 : lm2; } +/* Find the next fixed channel in BT_LISTEN state, continue iteration + * from an existing channel in the list or from the beginning of the + * global list (by passing NULL as first parameter). + */ +static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c, + bdaddr_t *src, u8 link_type) +{ + read_lock(&chan_list_lock); + + if (c) + c = list_next_entry(c, global_l); + else + c = list_entry(chan_list.next, typeof(*c), global_l); + + list_for_each_entry_from(c, &chan_list, global_l) { + if (c->chan_type != L2CAP_CHAN_FIXED) + continue; + if (c->state != BT_LISTEN) + continue; + if (bacmp(&c->src, src) && bacmp(&c->src, BDADDR_ANY)) + continue; + if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR) + continue; + if (link_type == LE_LINK && c->src_type == BDADDR_BREDR) + continue; + + l2cap_chan_hold(c); + read_unlock(&chan_list_lock); + return c; + } + + read_unlock(&chan_list_lock); + + return NULL; +} + void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { + struct hci_dev *hdev = hcon->hdev; struct l2cap_conn *conn; + struct l2cap_chan *pchan; + u8 dst_type; BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); - if (!status) { - conn = l2cap_conn_add(hcon); - if (conn) - l2cap_conn_ready(conn); - } else { + if (status) { l2cap_conn_del(hcon, bt_to_errno(status)); + return; } + + conn = l2cap_conn_add(hcon); + if (!conn) + return; + + dst_type = bdaddr_type(hcon, hcon->dst_type); + + /* If device is blocked, do not create channels for it */ + if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type)) + return; + + /* Find fixed channels and notify them of the new connection. We + * use multiple individual lookups, continuing each time where + * we left off, because the list lock would prevent calling the + * potentially sleeping l2cap_chan_lock() function. + */ + pchan = l2cap_global_fixed_chan(NULL, &hdev->bdaddr, hcon->type); + while (pchan) { + struct l2cap_chan *chan, *next; + + /* Client fixed channels should override server ones */ + if (__l2cap_get_chan_by_dcid(conn, pchan->scid)) + goto next; + + l2cap_chan_lock(pchan); + chan = pchan->ops->new_connection(pchan); + if (chan) { + bacpy(&chan->src, &hcon->src); + bacpy(&chan->dst, &hcon->dst); + chan->src_type = bdaddr_type(hcon, hcon->src_type); + chan->dst_type = dst_type; + + __l2cap_chan_add(conn, chan); + } + + l2cap_chan_unlock(pchan); +next: + next = l2cap_global_fixed_chan(pchan, &hdev->bdaddr, + hcon->type); + l2cap_chan_put(pchan); + pchan = next; + } + + l2cap_conn_ready(conn); } int l2cap_disconn_ind(struct hci_conn *hcon) @@ -7299,12 +7289,6 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt); - if (hcon->type == LE_LINK) { - if (!status && encrypt) - smp_distribute_keys(conn); - cancel_delayed_work(&conn->security_timer); - } - mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { @@ -7318,15 +7302,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (chan->scid == L2CAP_CID_ATT) { - if (!status && encrypt) { - chan->sec_level = hcon->sec_level; - l2cap_chan_ready(chan); - } - - l2cap_chan_unlock(chan); - continue; - } + if (!status && encrypt) + chan->sec_level = hcon->sec_level; if (!__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 1884f72083c2..ed06f88e6f10 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -99,15 +99,6 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) if (!bdaddr_type_is_valid(la.l2_bdaddr_type)) return -EINVAL; - if (la.l2_cid) { - /* When the socket gets created it defaults to - * CHAN_CONN_ORIENTED, so we need to overwrite the - * default here. - */ - chan->chan_type = L2CAP_CHAN_FIXED; - chan->omtu = L2CAP_DEFAULT_MTU; - } - if (bdaddr_type_is_le(la.l2_bdaddr_type)) { /* We only allow ATT user space socket */ if (la.l2_cid && @@ -790,6 +781,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, if (chan->scid == L2CAP_CID_ATT) { if (smp_conn_security(conn->hcon, sec.level)) break; + set_bit(FLAG_PENDING_SECURITY, &chan->flags); sk->sk_state = BT_CONFIG; chan->state = BT_CONFIG; @@ -1359,6 +1351,11 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; + if (test_and_clear_bit(FLAG_PENDING_SECURITY, &chan->flags)) { + sk->sk_state = BT_CONNECTED; + chan->state = BT_CONNECTED; + } + clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); sk->sk_state_change(sk); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b8554d429d88..c2457435a670 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -129,9 +129,6 @@ static const u16 mgmt_events[] = { #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) -#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ - !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - struct pending_cmd { struct list_head list; u16 opcode; @@ -1536,9 +1533,11 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status) /* When the discoverable mode gets changed, make sure * that class of device has the limited discoverable - * bit correctly set. + * bit correctly set. Also update page scan based on whitelist + * entries. */ hci_req_init(&req, hdev); + hci_update_page_scan(hdev, &req); update_class(&req); hci_req_run(&req, NULL); @@ -1785,6 +1784,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) if (conn_changed || discov_changed) { new_settings(hdev, cmd->sk); + hci_update_page_scan(hdev, NULL); if (discov_changed) mgmt_update_adv_data(hdev); hci_update_background_scan(hdev); @@ -1818,6 +1818,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev, return err; if (changed) { + hci_update_page_scan(hdev, NULL); hci_update_background_scan(hdev); return new_settings(hdev, sk); } @@ -4381,27 +4382,6 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, return err; } -static void set_bredr_scan(struct hci_request *req) -{ - struct hci_dev *hdev = req->hdev; - u8 scan = 0; - - /* Ensure that fast connectable is disabled. This function will - * not do anything if the page scan parameters are already what - * they should be. - */ - write_fast_connectable(req, false); - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || - !list_empty(&hdev->whitelist)) - scan |= SCAN_PAGE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - if (scan) - hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); -} - static void set_bredr_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; @@ -4507,9 +4487,8 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_req_init(&req, hdev); - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || - !list_empty(&hdev->whitelist)) - set_bredr_scan(&req); + write_fast_connectable(&req, false); + hci_update_page_scan(hdev, &req); /* Since only the advertising data flags will change, there * is no need to update the scan response data. @@ -5235,27 +5214,6 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data, return err; } -/* Helper for Add/Remove Device commands */ -static void update_page_scan(struct hci_dev *hdev, u8 scan) -{ - if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) - return; - - if (!hdev_is_powered(hdev)) - return; - - /* If HCI_CONNECTABLE is set then Add/Remove Device should not - * make any changes to page scanning. - */ - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - return; - - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); -} - static void device_added(struct sock *sk, struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u8 action) { @@ -5291,8 +5249,6 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (cp->addr.type == BDADDR_BREDR) { - bool update_scan; - /* Only incoming connections action is supported for now */ if (cp->action != 0x01) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, @@ -5301,15 +5257,12 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - update_scan = list_empty(&hdev->whitelist); - err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr, cp->addr.type); if (err) goto unlock; - if (update_scan) - update_page_scan(hdev, SCAN_PAGE); + hci_update_page_scan(hdev, NULL); goto added; } @@ -5392,8 +5345,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (list_empty(&hdev->whitelist)) - update_page_scan(hdev, SCAN_DISABLED); + hci_update_page_scan(hdev, NULL); device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); @@ -5444,7 +5396,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, kfree(b); } - update_page_scan(hdev, SCAN_DISABLED); + hci_update_page_scan(hdev, NULL); list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_DISABLED) @@ -5969,8 +5921,8 @@ static int powered_update_hci(struct hci_dev *hdev) sizeof(link_sec), &link_sec); if (lmp_bredr_capable(hdev)) { - if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) - set_bredr_scan(&req); + write_fast_connectable(&req, false); + hci_update_page_scan(hdev, &req); update_class(&req); update_name(&req); update_eir(&req); @@ -6281,25 +6233,35 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } +bool mgmt_powering_down(struct hci_dev *hdev) +{ + struct pending_cmd *cmd; + struct mgmt_mode *cp; + + cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev); + if (!cmd) + return false; + + cp = cmd->param; + if (!cp->val) + return true; + + return false; +} + void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 reason, bool mgmt_connected) { struct mgmt_ev_device_disconnected ev; - struct pending_cmd *power_off; struct sock *sk = NULL; - power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev); - if (power_off) { - struct mgmt_mode *cp = power_off->param; - - /* The connection is still in hci_conn_hash so test for 1 - * instead of 0 to know if this is the last one. - */ - if (!cp->val && hci_conn_count(hdev) == 1) { - cancel_delayed_work(&hdev->power_off); - queue_work(hdev->req_workqueue, &hdev->power_off.work); - } + /* The connection is still in hci_conn_hash so test for 1 + * instead of 0 to know if this is the last one. + */ + if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) { + cancel_delayed_work(&hdev->power_off); + queue_work(hdev->req_workqueue, &hdev->power_off.work); } if (!mgmt_connected) @@ -6359,19 +6321,13 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u8 status) { struct mgmt_ev_connect_failed ev; - struct pending_cmd *power_off; - power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev); - if (power_off) { - struct mgmt_mode *cp = power_off->param; - - /* The connection is still in hci_conn_hash so test for 1 - * instead of 0 to know if this is the last one. - */ - if (!cp->val && hci_conn_count(hdev) == 1) { - cancel_delayed_work(&hdev->power_off); - queue_work(hdev->req_workqueue, &hdev->power_off.work); - } + /* The connection is still in hci_conn_hash so test for 1 + * instead of 0 to know if this is the last one. + */ + if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) { + cancel_delayed_work(&hdev->power_off); + queue_work(hdev->req_workqueue, &hdev->power_off.work); } bacpy(&ev.addr.bdaddr, bdaddr); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index fd3294300803..07ca4ce0943b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -44,7 +44,10 @@ enum { }; struct smp_chan { - struct l2cap_conn *conn; + struct l2cap_conn *conn; + struct delayed_work security_timer; + struct work_struct distribute_work; + u8 preq[7]; /* SMP Pairing Request */ u8 prsp[7]; /* SMP Pairing Response */ u8 prnd[16]; /* SMP Pairing Random (local) */ @@ -139,12 +142,18 @@ static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3]) return 0; } -bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16], - bdaddr_t *bdaddr) +bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr) { + struct l2cap_chan *chan = hdev->smp_data; + struct crypto_blkcipher *tfm; u8 hash[3]; int err; + if (!chan || !chan->data) + return false; + + tfm = chan->data; + BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk); err = smp_ah(tfm, irk, &bdaddr->b[3], hash); @@ -154,10 +163,17 @@ bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16], return !memcmp(bdaddr->b, hash, 3); } -int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa) +int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa) { + struct l2cap_chan *chan = hdev->smp_data; + struct crypto_blkcipher *tfm; int err; + if (!chan || !chan->data) + return -EOPNOTSUPP; + + tfm = chan->data; + get_random_bytes(&rpa->b[3], 3); rpa->b[5] &= 0x3f; /* Clear two most significant bits */ @@ -235,47 +251,39 @@ static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16], return err; } -static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, - u16 dlen, void *data) -{ - struct sk_buff *skb; - struct l2cap_hdr *lh; - int len; - - len = L2CAP_HDR_SIZE + sizeof(code) + dlen; - - if (len > conn->mtu) - return NULL; - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return NULL; - - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(sizeof(code) + dlen); - lh->cid = cpu_to_le16(L2CAP_CID_SMP); - - memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); - - memcpy(skb_put(skb, dlen), data, dlen); - - return skb; -} - static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) { - struct sk_buff *skb = smp_build_cmd(conn, code, len, data); + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp; + struct kvec iv[2]; + struct msghdr msg; + + if (!chan) + return; BT_DBG("code 0x%2.2x", code); - if (!skb) + iv[0].iov_base = &code; + iv[0].iov_len = 1; + + iv[1].iov_base = data; + iv[1].iov_len = len; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_iov = (struct iovec *) &iv; + msg.msg_iovlen = 2; + + l2cap_chan_send(chan, &msg, 1 + len); + + if (!chan->data) return; - skb->priority = HCI_PRIO_MAX; - hci_send_acl(conn->hchan, skb, 0); + smp = chan->data; - cancel_delayed_work_sync(&conn->security_timer); - schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT); + cancel_delayed_work_sync(&smp->security_timer); + if (test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) + schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT); } static __u8 authreq_to_seclevel(__u8 authreq) @@ -302,7 +310,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn, struct smp_cmd_pairing *req, struct smp_cmd_pairing *rsp, __u8 authreq) { - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; u8 local_dist = 0, remote_dist = 0; @@ -345,7 +354,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn, static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) { - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || (max_key_size < SMP_MIN_ENC_KEY_SIZE)) @@ -356,9 +366,61 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) return 0; } +static void smp_chan_destroy(struct l2cap_conn *conn) +{ + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; + bool complete; + + BUG_ON(!smp); + + cancel_delayed_work_sync(&smp->security_timer); + /* In case the timeout freed the SMP context */ + if (!chan->data) + return; + + if (work_pending(&smp->distribute_work)) { + cancel_work_sync(&smp->distribute_work); + if (!chan->data) + return; + } + + complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags); + mgmt_smp_complete(conn->hcon, complete); + + kfree(smp->csrk); + kfree(smp->slave_csrk); + + crypto_free_blkcipher(smp->tfm_aes); + + /* If pairing failed clean up any keys we might have */ + if (!complete) { + if (smp->ltk) { + list_del(&smp->ltk->list); + kfree(smp->ltk); + } + + if (smp->slave_ltk) { + list_del(&smp->slave_ltk->list); + kfree(smp->slave_ltk); + } + + if (smp->remote_irk) { + list_del(&smp->remote_irk->list); + kfree(smp->remote_irk); + } + } + + chan->data = NULL; + kfree(smp); + hci_conn_drop(conn->hcon); +} + static void smp_failure(struct l2cap_conn *conn, u8 reason) { struct hci_conn *hcon = conn->hcon; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp; if (reason) smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), @@ -368,7 +430,10 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason) mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type, HCI_ERROR_AUTH_FAILURE); - cancel_delayed_work_sync(&conn->security_timer); + if (!chan->data) + return; + + smp = chan->data; if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) smp_chan_destroy(conn); @@ -405,7 +470,8 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, u8 local_io, u8 remote_io) { struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; u8 method; u32 passkey = 0; int ret = 0; @@ -574,8 +640,201 @@ static u8 smp_random(struct smp_chan *smp) return 0; } +static void smp_notify_keys(struct l2cap_conn *conn) +{ + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; + struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; + struct smp_cmd_pairing *req = (void *) &smp->preq[1]; + struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1]; + bool persistent; + + if (smp->remote_irk) { + mgmt_new_irk(hdev, smp->remote_irk); + /* Now that user space can be considered to know the + * identity address track the connection based on it + * from now on. + */ + bacpy(&hcon->dst, &smp->remote_irk->bdaddr); + hcon->dst_type = smp->remote_irk->addr_type; + l2cap_conn_update_id_addr(hcon); + + /* When receiving an indentity resolving key for + * a remote device that does not use a resolvable + * private address, just remove the key so that + * it is possible to use the controller white + * list for scanning. + * + * Userspace will have been told to not store + * this key at this point. So it is safe to + * just remove it. + */ + if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) { + list_del(&smp->remote_irk->list); + kfree(smp->remote_irk); + smp->remote_irk = NULL; + } + } + + /* The LTKs and CSRKs should be persistent only if both sides + * had the bonding bit set in their authentication requests. + */ + persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING); + + if (smp->csrk) { + smp->csrk->bdaddr_type = hcon->dst_type; + bacpy(&smp->csrk->bdaddr, &hcon->dst); + mgmt_new_csrk(hdev, smp->csrk, persistent); + } + + if (smp->slave_csrk) { + smp->slave_csrk->bdaddr_type = hcon->dst_type; + bacpy(&smp->slave_csrk->bdaddr, &hcon->dst); + mgmt_new_csrk(hdev, smp->slave_csrk, persistent); + } + + if (smp->ltk) { + smp->ltk->bdaddr_type = hcon->dst_type; + bacpy(&smp->ltk->bdaddr, &hcon->dst); + mgmt_new_ltk(hdev, smp->ltk, persistent); + } + + if (smp->slave_ltk) { + smp->slave_ltk->bdaddr_type = hcon->dst_type; + bacpy(&smp->slave_ltk->bdaddr, &hcon->dst); + mgmt_new_ltk(hdev, smp->slave_ltk, persistent); + } +} + +static void smp_distribute_keys(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, + distribute_work); + struct smp_cmd_pairing *req, *rsp; + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; + __u8 *keydist; + + BT_DBG("conn %p", conn); + + if (!test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) + return; + + rsp = (void *) &smp->prsp[1]; + + /* The responder sends its keys first */ + if (hcon->out && (smp->remote_key_dist & 0x07)) + return; + + req = (void *) &smp->preq[1]; + + if (hcon->out) { + keydist = &rsp->init_key_dist; + *keydist &= req->init_key_dist; + } else { + keydist = &rsp->resp_key_dist; + *keydist &= req->resp_key_dist; + } + + BT_DBG("keydist 0x%x", *keydist); + + if (*keydist & SMP_DIST_ENC_KEY) { + struct smp_cmd_encrypt_info enc; + struct smp_cmd_master_ident ident; + struct smp_ltk *ltk; + u8 authenticated; + __le16 ediv; + __le64 rand; + + get_random_bytes(enc.ltk, sizeof(enc.ltk)); + get_random_bytes(&ediv, sizeof(ediv)); + get_random_bytes(&rand, sizeof(rand)); + + smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); + + authenticated = hcon->sec_level == BT_SECURITY_HIGH; + ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, + SMP_LTK_SLAVE, authenticated, enc.ltk, + smp->enc_key_size, ediv, rand); + smp->slave_ltk = ltk; + + ident.ediv = ediv; + ident.rand = rand; + + smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); + + *keydist &= ~SMP_DIST_ENC_KEY; + } + + if (*keydist & SMP_DIST_ID_KEY) { + struct smp_cmd_ident_addr_info addrinfo; + struct smp_cmd_ident_info idinfo; + + memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk)); + + smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); + + /* The hci_conn contains the local identity address + * after the connection has been established. + * + * This is true even when the connection has been + * established using a resolvable random address. + */ + bacpy(&addrinfo.bdaddr, &hcon->src); + addrinfo.addr_type = hcon->src_type; + + smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), + &addrinfo); + + *keydist &= ~SMP_DIST_ID_KEY; + } + + if (*keydist & SMP_DIST_SIGN) { + struct smp_cmd_sign_info sign; + struct smp_csrk *csrk; + + /* Generate a new random key */ + get_random_bytes(sign.csrk, sizeof(sign.csrk)); + + csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); + if (csrk) { + csrk->master = 0x00; + memcpy(csrk->val, sign.csrk, sizeof(csrk->val)); + } + smp->slave_csrk = csrk; + + smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); + + *keydist &= ~SMP_DIST_SIGN; + } + + /* If there are still keys to be received wait for them */ + if ((smp->remote_key_dist & 0x07)) + return; + + clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags); + set_bit(SMP_FLAG_COMPLETE, &smp->flags); + smp_notify_keys(conn); + + smp_chan_destroy(conn); +} + +static void smp_timeout(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, + security_timer.work); + struct l2cap_conn *conn = smp->conn; + + BT_DBG("conn %p", conn); + + l2cap_conn_shutdown(conn, ETIMEDOUT); +} + static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) { + struct l2cap_chan *chan = conn->smp; struct smp_chan *smp; smp = kzalloc(sizeof(*smp), GFP_ATOMIC); @@ -593,54 +852,20 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) } smp->conn = conn; - conn->smp_chan = smp; + chan->data = smp; + + INIT_WORK(&smp->distribute_work, smp_distribute_keys); + INIT_DELAYED_WORK(&smp->security_timer, smp_timeout); hci_conn_hold(conn->hcon); return smp; } -void smp_chan_destroy(struct l2cap_conn *conn) -{ - struct smp_chan *smp = conn->smp_chan; - bool complete; - - BUG_ON(!smp); - - complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags); - mgmt_smp_complete(conn->hcon, complete); - - kfree(smp->csrk); - kfree(smp->slave_csrk); - - crypto_free_blkcipher(smp->tfm_aes); - - /* If pairing failed clean up any keys we might have */ - if (!complete) { - if (smp->ltk) { - list_del(&smp->ltk->list); - kfree(smp->ltk); - } - - if (smp->slave_ltk) { - list_del(&smp->slave_ltk->list); - kfree(smp->slave_ltk); - } - - if (smp->remote_irk) { - list_del(&smp->remote_irk->list); - kfree(smp->remote_irk); - } - } - - kfree(smp); - conn->smp_chan = NULL; - hci_conn_drop(conn->hcon); -} - int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) { struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_chan *chan; struct smp_chan *smp; u32 value; @@ -649,7 +874,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) if (!conn || !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return -ENOTCONN; - smp = conn->smp_chan; + chan = conn->smp; + if (!chan) + return -ENOTCONN; + + smp = chan->data; switch (mgmt_op) { case MGMT_OP_USER_PASSKEY_REPLY: @@ -696,10 +925,12 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (conn->hcon->role != HCI_ROLE_SLAVE) return SMP_CMD_NOTSUPP; - if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) + if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { smp = smp_chan_create(conn); - else - smp = conn->smp_chan; + } else { + struct l2cap_chan *chan = conn->smp; + smp = chan->data; + } if (!smp) return SMP_UNSPECIFIED; @@ -753,7 +984,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; u8 key_size, auth = SMP_AUTH_NONE; int ret; @@ -814,7 +1046,8 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); @@ -837,7 +1070,8 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; BT_DBG("conn %p", conn); @@ -1010,7 +1244,8 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_encrypt_info *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; BT_DBG("conn %p", conn); @@ -1031,7 +1266,8 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_master_ident *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; struct hci_dev *hdev = conn->hcon->hdev; struct hci_conn *hcon = conn->hcon; struct smp_ltk *ltk; @@ -1058,7 +1294,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) rp->ediv, rp->rand); smp->ltk = ltk; if (!(smp->remote_key_dist & SMP_DIST_ID_KEY)) - smp_distribute_keys(conn); + queue_work(hdev->workqueue, &smp->distribute_work); hci_dev_unlock(hdev); return 0; @@ -1067,7 +1303,8 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_ident_info *info = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; BT_DBG(""); @@ -1089,8 +1326,10 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_ident_addr_info *info = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; bdaddr_t rpa; BT_DBG(""); @@ -1133,7 +1372,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, smp->id_addr_type, smp->irk, &rpa); distribute: - smp_distribute_keys(conn); + queue_work(hdev->workqueue, &smp->distribute_work); hci_dev_unlock(hcon->hdev); @@ -1143,7 +1382,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_sign_info *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; + struct l2cap_chan *chan = conn->smp; + struct smp_chan *smp = chan->data; struct hci_dev *hdev = conn->hcon->hdev; struct smp_csrk *csrk; @@ -1168,15 +1408,15 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(csrk->val, rp->csrk, sizeof(csrk->val)); } smp->csrk = csrk; - if (!(smp->remote_key_dist & SMP_DIST_SIGN)) - smp_distribute_keys(conn); + queue_work(hdev->workqueue, &smp->distribute_work); hci_dev_unlock(hdev); return 0; } -int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) +static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb) { + struct l2cap_conn *conn = chan->conn; struct hci_conn *hcon = conn->hcon; __u8 code, reason; int err = 0; @@ -1186,10 +1426,8 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } - if (skb->len < 1) { - kfree_skb(skb); + if (skb->len < 1) return -EILSEQ; - } if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) { err = -EOPNOTSUPP; @@ -1207,10 +1445,11 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) * returns an error). */ if (code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ && - !conn->smp_chan) { + !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) { BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code); - kfree_skb(skb); - return -EOPNOTSUPP; + reason = SMP_CMD_NOTSUPP; + err = -EOPNOTSUPP; + goto done; } switch (code) { @@ -1271,188 +1510,201 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) done: if (reason) smp_failure(conn, reason); - - kfree_skb(skb); + if (!err) + kfree_skb(skb); return err; } -static void smp_notify_keys(struct l2cap_conn *conn) +static void smp_teardown_cb(struct l2cap_chan *chan, int err) { - struct smp_chan *smp = conn->smp_chan; - struct hci_conn *hcon = conn->hcon; - struct hci_dev *hdev = hcon->hdev; - struct smp_cmd_pairing *req = (void *) &smp->preq[1]; - struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1]; - bool persistent; + struct l2cap_conn *conn = chan->conn; - if (smp->remote_irk) { - mgmt_new_irk(hdev, smp->remote_irk); - /* Now that user space can be considered to know the - * identity address track the connection based on it - * from now on. - */ - bacpy(&hcon->dst, &smp->remote_irk->bdaddr); - hcon->dst_type = smp->remote_irk->addr_type; - l2cap_conn_update_id_addr(hcon); + BT_DBG("chan %p", chan); - /* When receiving an indentity resolving key for - * a remote device that does not use a resolvable - * private address, just remove the key so that - * it is possible to use the controller white - * list for scanning. - * - * Userspace will have been told to not store - * this key at this point. So it is safe to - * just remove it. - */ - if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) { - list_del(&smp->remote_irk->list); - kfree(smp->remote_irk); - smp->remote_irk = NULL; - } - } + if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) + smp_chan_destroy(conn); - /* The LTKs and CSRKs should be persistent only if both sides - * had the bonding bit set in their authentication requests. - */ - persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING); - - if (smp->csrk) { - smp->csrk->bdaddr_type = hcon->dst_type; - bacpy(&smp->csrk->bdaddr, &hcon->dst); - mgmt_new_csrk(hdev, smp->csrk, persistent); - } - - if (smp->slave_csrk) { - smp->slave_csrk->bdaddr_type = hcon->dst_type; - bacpy(&smp->slave_csrk->bdaddr, &hcon->dst); - mgmt_new_csrk(hdev, smp->slave_csrk, persistent); - } - - if (smp->ltk) { - smp->ltk->bdaddr_type = hcon->dst_type; - bacpy(&smp->ltk->bdaddr, &hcon->dst); - mgmt_new_ltk(hdev, smp->ltk, persistent); - } - - if (smp->slave_ltk) { - smp->slave_ltk->bdaddr_type = hcon->dst_type; - bacpy(&smp->slave_ltk->bdaddr, &hcon->dst); - mgmt_new_ltk(hdev, smp->slave_ltk, persistent); - } + conn->smp = NULL; + l2cap_chan_put(chan); } -int smp_distribute_keys(struct l2cap_conn *conn) +static void smp_resume_cb(struct l2cap_chan *chan) { - struct smp_cmd_pairing *req, *rsp; - struct smp_chan *smp = conn->smp_chan; + struct smp_chan *smp = chan->data; + struct l2cap_conn *conn = chan->conn; struct hci_conn *hcon = conn->hcon; struct hci_dev *hdev = hcon->hdev; - __u8 *keydist; - BT_DBG("conn %p", conn); + BT_DBG("chan %p", chan); - if (!test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) - return 0; + if (!smp) + return; - rsp = (void *) &smp->prsp[1]; + cancel_delayed_work(&smp->security_timer); - /* The responder sends its keys first */ - if (hcon->out && (smp->remote_key_dist & 0x07)) - return 0; + if (test_bit(HCI_CONN_ENCRYPT, &hcon->flags)) + queue_work(hdev->workqueue, &smp->distribute_work); +} - req = (void *) &smp->preq[1]; +static void smp_ready_cb(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; - if (hcon->out) { - keydist = &rsp->init_key_dist; - *keydist &= req->init_key_dist; - } else { - keydist = &rsp->resp_key_dist; - *keydist &= req->resp_key_dist; + BT_DBG("chan %p", chan); + + conn->smp = chan; + l2cap_chan_hold(chan); +} + +static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) +{ + int err; + + BT_DBG("chan %p", chan); + + err = smp_sig_channel(chan, skb); + if (err) { + struct smp_chan *smp = chan->data; + + if (smp) + cancel_delayed_work_sync(&smp->security_timer); + + l2cap_conn_shutdown(chan->conn, -err); } - BT_DBG("keydist 0x%x", *keydist); + return err; +} - if (*keydist & SMP_DIST_ENC_KEY) { - struct smp_cmd_encrypt_info enc; - struct smp_cmd_master_ident ident; - struct smp_ltk *ltk; - u8 authenticated; - __le16 ediv; - __le64 rand; +static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, + unsigned long len, int nb) +{ + struct sk_buff *skb; - get_random_bytes(enc.ltk, sizeof(enc.ltk)); - get_random_bytes(&ediv, sizeof(ediv)); - get_random_bytes(&rand, sizeof(rand)); + skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL); + if (!skb) + return ERR_PTR(-ENOMEM); - smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); + skb->priority = HCI_PRIO_MAX; + bt_cb(skb)->chan = chan; - authenticated = hcon->sec_level == BT_SECURITY_HIGH; - ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, - SMP_LTK_SLAVE, authenticated, enc.ltk, - smp->enc_key_size, ediv, rand); - smp->slave_ltk = ltk; + return skb; +} - ident.ediv = ediv; - ident.rand = rand; +static const struct l2cap_ops smp_chan_ops = { + .name = "Security Manager", + .ready = smp_ready_cb, + .recv = smp_recv_cb, + .alloc_skb = smp_alloc_skb_cb, + .teardown = smp_teardown_cb, + .resume = smp_resume_cb, - smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); + .new_connection = l2cap_chan_no_new_connection, + .state_change = l2cap_chan_no_state_change, + .close = l2cap_chan_no_close, + .defer = l2cap_chan_no_defer, + .suspend = l2cap_chan_no_suspend, + .set_shutdown = l2cap_chan_no_set_shutdown, + .get_sndtimeo = l2cap_chan_no_get_sndtimeo, + .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, +}; - *keydist &= ~SMP_DIST_ENC_KEY; +static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + BT_DBG("pchan %p", pchan); + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + chan->chan_type = pchan->chan_type; + chan->ops = &smp_chan_ops; + chan->scid = pchan->scid; + chan->dcid = chan->scid; + chan->imtu = pchan->imtu; + chan->omtu = pchan->omtu; + chan->mode = pchan->mode; + + BT_DBG("created chan %p", chan); + + return chan; +} + +static const struct l2cap_ops smp_root_chan_ops = { + .name = "Security Manager Root", + .new_connection = smp_new_conn_cb, + + /* None of these are implemented for the root channel */ + .close = l2cap_chan_no_close, + .alloc_skb = l2cap_chan_no_alloc_skb, + .recv = l2cap_chan_no_recv, + .state_change = l2cap_chan_no_state_change, + .teardown = l2cap_chan_no_teardown, + .ready = l2cap_chan_no_ready, + .defer = l2cap_chan_no_defer, + .suspend = l2cap_chan_no_suspend, + .resume = l2cap_chan_no_resume, + .set_shutdown = l2cap_chan_no_set_shutdown, + .get_sndtimeo = l2cap_chan_no_get_sndtimeo, + .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, +}; + +int smp_register(struct hci_dev *hdev) +{ + struct l2cap_chan *chan; + struct crypto_blkcipher *tfm_aes; + + BT_DBG("%s", hdev->name); + + tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm_aes)) { + int err = PTR_ERR(tfm_aes); + BT_ERR("Unable to create crypto context"); + return err; } - if (*keydist & SMP_DIST_ID_KEY) { - struct smp_cmd_ident_addr_info addrinfo; - struct smp_cmd_ident_info idinfo; - - memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk)); - - smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); - - /* The hci_conn contains the local identity address - * after the connection has been established. - * - * This is true even when the connection has been - * established using a resolvable random address. - */ - bacpy(&addrinfo.bdaddr, &hcon->src); - addrinfo.addr_type = hcon->src_type; - - smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), - &addrinfo); - - *keydist &= ~SMP_DIST_ID_KEY; + chan = l2cap_chan_create(); + if (!chan) { + crypto_free_blkcipher(tfm_aes); + return -ENOMEM; } - if (*keydist & SMP_DIST_SIGN) { - struct smp_cmd_sign_info sign; - struct smp_csrk *csrk; + chan->data = tfm_aes; - /* Generate a new random key */ - get_random_bytes(sign.csrk, sizeof(sign.csrk)); + l2cap_add_scid(chan, L2CAP_CID_SMP); - csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); - if (csrk) { - csrk->master = 0x00; - memcpy(csrk->val, sign.csrk, sizeof(csrk->val)); - } - smp->slave_csrk = csrk; + l2cap_chan_set_defaults(chan); - smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); + bacpy(&chan->src, &hdev->bdaddr); + chan->src_type = BDADDR_LE_PUBLIC; + chan->state = BT_LISTEN; + chan->mode = L2CAP_MODE_BASIC; + chan->imtu = L2CAP_DEFAULT_MTU; + chan->ops = &smp_root_chan_ops; - *keydist &= ~SMP_DIST_SIGN; - } - - /* If there are still keys to be received wait for them */ - if ((smp->remote_key_dist & 0x07)) - return 0; - - clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags); - cancel_delayed_work_sync(&conn->security_timer); - set_bit(SMP_FLAG_COMPLETE, &smp->flags); - smp_notify_keys(conn); - - smp_chan_destroy(conn); + hdev->smp_data = chan; return 0; } + +void smp_unregister(struct hci_dev *hdev) +{ + struct l2cap_chan *chan = hdev->smp_data; + struct crypto_blkcipher *tfm_aes; + + if (!chan) + return; + + BT_DBG("%s chan %p", hdev->name, chan); + + tfm_aes = chan->data; + if (tfm_aes) { + chan->data = NULL; + crypto_free_blkcipher(tfm_aes); + } + + hdev->smp_data = NULL; + l2cap_chan_put(chan); +} diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 796f4f45f92f..cf1094617c69 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -126,14 +126,12 @@ enum { /* SMP Commands */ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level); int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); -int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); -int smp_distribute_keys(struct l2cap_conn *conn); int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); -void smp_chan_destroy(struct l2cap_conn *conn); +bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr); +int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa); -bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16], - bdaddr_t *bdaddr); -int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa); +int smp_register(struct hci_dev *hdev); +void smp_unregister(struct hci_dev *hdev); #endif /* __SMP_H */ diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 6591d27e53a4..5e788cdc499a 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -77,14 +77,6 @@ lowpan_dev_info *lowpan_dev_info(const struct net_device *dev) return netdev_priv(dev); } -static inline void lowpan_address_flip(u8 *src, u8 *dest) -{ - int i; - - for (i = 0; i < IEEE802154_ADDR_LEN; i++) - (dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i]; -} - static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *_daddr, const void *_saddr, unsigned int len) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 927b4ea0128b..4d8989b87960 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1011,15 +1011,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } - if (mask & BIT(NL80211_STA_FLAG_WME)) { - if (set & BIT(NL80211_STA_FLAG_WME)) { - set_sta_flag(sta, WLAN_STA_WME); - sta->sta.wme = true; - } else { - clear_sta_flag(sta, WLAN_STA_WME); - sta->sta.wme = false; - } - } + if (mask & BIT(NL80211_STA_FLAG_WME)) + sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); if (mask & BIT(NL80211_STA_FLAG_MFP)) { if (set & BIT(NL80211_STA_FLAG_MFP)) @@ -3352,7 +3345,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, band = chanctx_conf->def.chan->band; sta = sta_info_get_bss(sdata, peer); if (sta) { - qos = test_sta_flag(sta, WLAN_STA_WME); + qos = sta->sta.wme; } else { rcu_read_unlock(); return -ENOLINK; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 399ad82c997f..4c74e8da64b9 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -549,12 +549,12 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, compat = cfg80211_chandef_compatible( &sdata->vif.bss_conf.chandef, compat); - if (!compat) + if (WARN_ON_ONCE(!compat)) break; } rcu_read_unlock(); - if (WARN_ON_ONCE(!compat)) + if (!compat) return; ieee80211_change_chanctx(local, ctx, compat); @@ -639,41 +639,6 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, return ret; } -static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx_conf *conf; - struct ieee80211_chanctx *ctx; - bool use_reserved_switch = false; - - lockdep_assert_held(&local->chanctx_mtx); - - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) - return; - - ctx = container_of(conf, struct ieee80211_chanctx, conf); - - if (sdata->reserved_chanctx) { - if (sdata->reserved_chanctx->replace_state == - IEEE80211_CHANCTX_REPLACES_OTHER && - ieee80211_chanctx_num_reserved(local, - sdata->reserved_chanctx) > 1) - use_reserved_switch = true; - - ieee80211_vif_unreserve_chanctx(sdata); - } - - ieee80211_assign_vif_chanctx(sdata, NULL); - if (ieee80211_chanctx_refcount(local, ctx) == 0) - ieee80211_free_chanctx(local, ctx); - - /* Unreserving may ready an in-place reservation. */ - if (use_reserved_switch) - ieee80211_vif_use_reserved_switch(local); -} - void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx) { @@ -764,63 +729,6 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); } -int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, - const struct cfg80211_chan_def *chandef, - enum ieee80211_chanctx_mode mode) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx *ctx; - u8 radar_detect_width = 0; - int ret; - - lockdep_assert_held(&local->mtx); - - WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); - - mutex_lock(&local->chanctx_mtx); - - ret = cfg80211_chandef_dfs_required(local->hw.wiphy, - chandef, - sdata->wdev.iftype); - if (ret < 0) - goto out; - if (ret > 0) - radar_detect_width = BIT(chandef->width); - - sdata->radar_required = ret; - - ret = ieee80211_check_combinations(sdata, chandef, mode, - radar_detect_width); - if (ret < 0) - goto out; - - __ieee80211_vif_release_channel(sdata); - - ctx = ieee80211_find_chanctx(local, chandef, mode); - if (!ctx) - ctx = ieee80211_new_chanctx(local, chandef, mode); - if (IS_ERR(ctx)) { - ret = PTR_ERR(ctx); - goto out; - } - - sdata->vif.bss_conf.chandef = *chandef; - - ret = ieee80211_assign_vif_chanctx(sdata, ctx); - if (ret) { - /* if assign fails refcount stays the same */ - if (ieee80211_chanctx_refcount(local, ctx) == 0) - ieee80211_free_chanctx(local, ctx); - goto out; - } - - ieee80211_recalc_smps_chanctx(local, ctx); - ieee80211_recalc_radar_chanctx(local, ctx); - out: - mutex_unlock(&local->chanctx_mtx); - return ret; -} - static void __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, bool clear) @@ -1269,8 +1177,7 @@ static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) return err; } -int -ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) +static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata, *sdata_tmp; struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; @@ -1522,6 +1429,98 @@ ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) return err; } +static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx *ctx; + bool use_reserved_switch = false; + + lockdep_assert_held(&local->chanctx_mtx); + + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) + return; + + ctx = container_of(conf, struct ieee80211_chanctx, conf); + + if (sdata->reserved_chanctx) { + if (sdata->reserved_chanctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER && + ieee80211_chanctx_num_reserved(local, + sdata->reserved_chanctx) > 1) + use_reserved_switch = true; + + ieee80211_vif_unreserve_chanctx(sdata); + } + + ieee80211_assign_vif_chanctx(sdata, NULL); + if (ieee80211_chanctx_refcount(local, ctx) == 0) + ieee80211_free_chanctx(local, ctx); + + /* Unreserving may ready an in-place reservation. */ + if (use_reserved_switch) + ieee80211_vif_use_reserved_switch(local); +} + +int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + enum ieee80211_chanctx_mode mode) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *ctx; + u8 radar_detect_width = 0; + int ret; + + lockdep_assert_held(&local->mtx); + + WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); + + mutex_lock(&local->chanctx_mtx); + + ret = cfg80211_chandef_dfs_required(local->hw.wiphy, + chandef, + sdata->wdev.iftype); + if (ret < 0) + goto out; + if (ret > 0) + radar_detect_width = BIT(chandef->width); + + sdata->radar_required = ret; + + ret = ieee80211_check_combinations(sdata, chandef, mode, + radar_detect_width); + if (ret < 0) + goto out; + + __ieee80211_vif_release_channel(sdata); + + ctx = ieee80211_find_chanctx(local, chandef, mode); + if (!ctx) + ctx = ieee80211_new_chanctx(local, chandef, mode); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto out; + } + + sdata->vif.bss_conf.chandef = *chandef; + + ret = ieee80211_assign_vif_chanctx(sdata, ctx); + if (ret) { + /* if assign fails refcount stays the same */ + if (ieee80211_chanctx_refcount(local, ctx) == 0) + ieee80211_free_chanctx(local, ctx); + goto out; + } + + ieee80211_recalc_smps_chanctx(local, ctx); + ieee80211_recalc_radar_chanctx(local, ctx); + out: + mutex_unlock(&local->chanctx_mtx); + return ret; +} + int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 86173c0de40e..33eb4a43a2f3 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -77,7 +77,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, TEST(AUTH), TEST(ASSOC), TEST(PS_STA), TEST(PS_DRIVER), TEST(AUTHORIZED), TEST(SHORT_PREAMBLE), - TEST(WME), TEST(WDS), TEST(CLEAR_PS_FILT), + sta->sta.wme ? "WME\n" : "", + TEST(WDS), TEST(CLEAR_PS_FILT), TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 9713dc54ea4b..5f9654d31a8d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1038,7 +1038,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } if (sta && elems->wmm_info) - set_sta_flag(sta, WLAN_STA_WME); + sta->sta.wme = true; if (sta && elems->ht_operation && elems->ht_cap_elem && sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ef7a089ac546..ffb20e5e6cf3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1869,7 +1869,6 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, int __must_check ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata); int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata); -int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local); int __must_check ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/key.c b/net/mac80211/key.c index d808cff80153..6429d0e1d4a1 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -130,9 +130,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) if (!ret) { key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; - if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || - (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + if (!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) sdata->crypto_tx_tailroom_needed_cnt--; WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && @@ -180,9 +178,7 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) sta = key->sta; sdata = key->sdata; - if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || - (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + if (!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) increment_tailroom_need_count(sdata); ret = drv_set_key(key->local, DISABLE_KEY, sdata, @@ -878,9 +874,7 @@ void ieee80211_remove_key(struct ieee80211_key_conf *keyconf) if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) { key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; - if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || - (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + if (!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) increment_tailroom_need_count(key->sdata); } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index cf032a8db9d7..a6699dceae7c 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -729,7 +729,7 @@ void mesh_plink_broken(struct sta_info *sta) tbl = rcu_dereference(mesh_paths); for_each_mesh_entry(tbl, node, i) { mpath = node->mpath; - if (rcu_dereference(mpath->next_hop) == sta && + if (rcu_access_pointer(mpath->next_hop) == sta && mpath->flags & MESH_PATH_ACTIVE && !(mpath->flags & MESH_PATH_FIXED)) { spin_lock_bh(&mpath->state_lock); @@ -794,7 +794,7 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta) tbl = resize_dereference_mesh_paths(); for_each_mesh_entry(tbl, node, i) { mpath = node->mpath; - if (rcu_dereference(mpath->next_hop) == sta) { + if (rcu_access_pointer(mpath->next_hop) == sta) { spin_lock(&tbl->hashwlock[i]); __mesh_path_del(tbl, node); spin_unlock(&tbl->hashwlock[i]); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index c47194d27149..b488e1859b18 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -431,14 +431,12 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) return NULL; sta->plink_state = NL80211_PLINK_LISTEN; + sta->sta.wme = true; sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); - set_sta_flag(sta, WLAN_STA_WME); - sta->sta.wme = true; - return sta; } @@ -1004,7 +1002,6 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode ftype; u32 changed = 0; u8 ie_len = elems->peering_len; - __le16 _plid, _llid; u16 plid, llid = 0; if (!elems->peering) { @@ -1039,13 +1036,10 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, /* Note the lines below are correct, the llid in the frame is the plid * from the point of view of this host. */ - memcpy(&_plid, PLINK_GET_LLID(elems->peering), sizeof(__le16)); - plid = le16_to_cpu(_plid); + plid = get_unaligned_le16(PLINK_GET_LLID(elems->peering)); if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || - (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) { - memcpy(&_llid, PLINK_GET_PLID(elems->peering), sizeof(__le16)); - llid = le16_to_cpu(_llid); - } + (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) + llid = get_unaligned_le16(PLINK_GET_PLID(elems->peering)); /* WARNING: Only for sta pointer, is dropped & re-acquired */ rcu_read_lock(); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b82a12a9f0f1..8a73de6a5f5b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -149,6 +149,7 @@ static u32 ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_channel *channel, + const struct ieee80211_ht_cap *ht_cap, const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_vht_operation *vht_oper, struct cfg80211_chan_def *chandef, bool tracking) @@ -162,13 +163,19 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, chandef->center_freq1 = channel->center_freq; chandef->center_freq2 = 0; - if (!ht_oper || !sband->ht_cap.ht_supported) { + if (!ht_cap || !ht_oper || !sband->ht_cap.ht_supported) { ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; goto out; } chandef->width = NL80211_CHAN_WIDTH_20; + if (!(ht_cap->cap_info & + cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) { + ret = IEEE80211_STA_DISABLE_40MHZ | IEEE80211_STA_DISABLE_VHT; + goto out; + } + ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, channel->band); /* check that channel matches the right operating channel */ @@ -328,6 +335,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, + const struct ieee80211_ht_cap *ht_cap, const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_vht_operation *vht_oper, const u8 *bssid, u32 *changed) @@ -367,8 +375,9 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[chan->band]; /* calculate new channel (type) based on HT/VHT operation IEs */ - flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper, - vht_oper, &chandef, true); + flags = ieee80211_determine_chantype(sdata, sband, chan, + ht_cap, ht_oper, vht_oper, + &chandef, true); /* * Downgrade the new channel if we associated with restricted @@ -2677,8 +2686,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) set_sta_flag(sta, WLAN_STA_MFP); - if (elems.wmm_param) - set_sta_flag(sta, WLAN_STA_WME); + sta->sta.wme = elems.wmm_param; err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) @@ -3174,7 +3182,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, bssid); - if (ieee80211_config_bw(sdata, sta, elems.ht_operation, + if (ieee80211_config_bw(sdata, sta, + elems.ht_cap_elem, elems.ht_operation, elems.vht_operation, bssid, &changed)) { mutex_unlock(&local->sta_mtx); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, @@ -3808,6 +3817,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const struct ieee80211_ht_cap *ht_cap = NULL; const struct ieee80211_ht_operation *ht_oper = NULL; const struct ieee80211_vht_operation *vht_oper = NULL; struct ieee80211_supported_band *sband; @@ -3824,14 +3834,17 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && sband->ht_cap.ht_supported) { - const u8 *ht_oper_ie, *ht_cap; + const u8 *ht_oper_ie, *ht_cap_ie; ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION); if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) ht_oper = (void *)(ht_oper_ie + 2); - ht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); - if (!ht_cap || ht_cap[1] < sizeof(struct ieee80211_ht_cap)) { + ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); + if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) + ht_cap = (void *)(ht_cap_ie + 2); + + if (!ht_cap) { ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ht_oper = NULL; } @@ -3862,7 +3875,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, cbss->channel, - ht_oper, vht_oper, + ht_cap, ht_oper, vht_oper, &chandef, false); sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bd2c9b22c945..a8d862f9183c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2725,7 +2725,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) sig = status->signal; if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig, - rx->skb->data, rx->skb->len, 0, GFP_ATOMIC)) { + rx->skb->data, rx->skb->len, 0)) { if (rx->sta) rx->sta->rx_packets++; dev_kfree_skb(rx->skb); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index a0a938145dcc..a9bb6eb8c3e0 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1094,7 +1094,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) if (rcu_access_pointer(local->sched_scan_sdata)) { ret = drv_sched_scan_stop(local, sdata); if (!ret) - rcu_assign_pointer(local->sched_scan_sdata, NULL); + RCU_INIT_POINTER(local->sched_scan_sdata, NULL); } out: mutex_unlock(&local->mtx); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 441875f03750..730030542024 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1182,7 +1182,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb; int size = sizeof(*nullfunc); __le16 fc; - bool qos = test_sta_flag(sta, WLAN_STA_WME); + bool qos = sta->sta.wme; struct ieee80211_tx_info *info; struct ieee80211_chanctx_conf *chanctx_conf; @@ -1837,7 +1837,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); - if (test_sta_flag(sta, WLAN_STA_WME)) + if (sta->sta.wme) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); if (test_sta_flag(sta, WLAN_STA_MFP)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index d411bcc8ef08..89c40d5c0633 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -31,7 +31,6 @@ * when virtual port control is not in use. * @WLAN_STA_SHORT_PREAMBLE: Station is capable of receiving short-preamble * frames. - * @WLAN_STA_WME: Station is a QoS-STA. * @WLAN_STA_WDS: Station is one of our WDS peers. * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next @@ -69,7 +68,6 @@ enum ieee80211_sta_info_flags { WLAN_STA_PS_STA, WLAN_STA_AUTHORIZED, WLAN_STA_SHORT_PREAMBLE, - WLAN_STA_WME, WLAN_STA_WDS, WLAN_STA_CLEAR_PS_FILT, WLAN_STA_MFP, diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 1b21050be174..f2cb3b6c1871 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -316,8 +316,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, } /* add the QoS param IE if both the peer and we support it */ - if (local->hw.queues >= IEEE80211_NUM_ACS && - test_sta_flag(sta, WLAN_STA_WME)) + if (local->hw.queues >= IEEE80211_NUM_ACS && sta->sta.wme) ieee80211_tdls_add_wmm_param_ie(sdata, skb); /* add any custom IEs that go before HT operation */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 464106c023d8..925c39f4099e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1478,7 +1478,10 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, tail_need = max_t(int, tail_need, 0); } - if (skb_cloned(skb)) + if (skb_cloned(skb) && + (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CLONED_SKBS) || + !skb_clone_writable(skb, ETH_HLEN) || + sdata->crypto_tx_tailroom_needed_cnt)) I802_DEBUG_INC(local->tx_expand_skb_head_cloned); else if (head_need || tail_need) I802_DEBUG_INC(local->tx_expand_skb_head); @@ -1844,7 +1847,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 30; authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); - wme_sta = test_sta_flag(sta, WLAN_STA_WME); + wme_sta = sta->sta.wme; } ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); @@ -1957,7 +1960,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); - wme_sta = test_sta_flag(sta, WLAN_STA_WME); + wme_sta = sta->sta.wme; tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER); tdls_auth = test_sta_flag(sta, @@ -2035,7 +2038,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, sta = sta_info_get(sdata, hdr.addr1); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); - wme_sta = test_sta_flag(sta, WLAN_STA_WME); + wme_sta = sta->sta.wme; } } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index d51422c778de..6459946f0b74 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -118,7 +118,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP_VLAN: sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { - qos = test_sta_flag(sta, WLAN_STA_WME); + qos = sta->sta.wme; break; } case NL80211_IFTYPE_AP: @@ -145,7 +145,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, if (!sta && ra && !is_multicast_ether_addr(ra)) { sta = sta_info_get(sdata, ra); if (sta) - qos = test_sta_flag(sta, WLAN_STA_WME); + qos = sta->sta.wme; } rcu_read_unlock(); diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 7f820a108a9c..a14cf9ede171 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -86,9 +86,8 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) static void mac802154_rx_worker(struct work_struct *work) { struct rx_work *rw = container_of(work, struct rx_work, work); - struct sk_buff *skb = rw->skb; - mac802154_subif_rx(rw->dev, skb, rw->lqi); + mac802154_subif_rx(rw->dev, rw->skb, rw->lqi); kfree(rw); } @@ -101,7 +100,7 @@ ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi) if (!skb) return; - work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC); + work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) return; diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 8124353646ae..fdf4c0e67259 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -89,8 +89,7 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, if (!(priv->phy->channels_supported[page] & (1 << chan))) { WARN_ON(1); - kfree_skb(skb); - return NETDEV_TX_OK; + goto err_tx; } mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb); @@ -103,12 +102,10 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, data[1] = crc >> 8; } - if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } + if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) + goto err_tx; - work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC); + work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { kfree_skb(skb); return NETDEV_TX_BUSY; @@ -129,4 +126,8 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, queue_work(priv->dev_workqueue, &work->work); return NETDEV_TX_OK; + +err_tx: + kfree_skb(skb); + return NETDEV_TX_OK; } diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 547838822d5e..b7961129ce4d 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -475,8 +475,7 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb, rc = mac802154_llsec_decrypt(&sdata->sec, skb); if (rc) { pr_debug("decryption failed: %i\n", rc); - kfree_skb(skb); - return NET_RX_DROP; + goto fail; } sdata->dev->stats.rx_packets++; @@ -488,9 +487,12 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb, default: pr_warn("ieee802154: bad frame received (type = %d)\n", mac_cb(skb)->type); - kfree_skb(skb); - return NET_RX_DROP; + goto fail; } + +fail: + kfree_skb(skb); + return NET_RX_DROP; } static void mac802154_print_addr(const char *name, diff --git a/net/wireless/core.c b/net/wireless/core.c index afee5e0455ea..c6620aa679e0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -492,12 +492,6 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; - /* - * There are major locking problems in nl80211/mac80211 for CSA, - * disable for all drivers until this has been reworked. - */ - wiphy->flags &= ~WIPHY_FLAG_HAS_CHANNEL_SWITCH; - #ifdef CONFIG_PM if (WARN_ON(wiphy->wowlan && (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && @@ -635,6 +629,9 @@ int wiphy_register(struct wiphy *wiphy) if (IS_ERR(rdev->wiphy.debugfsdir)) rdev->wiphy.debugfsdir = NULL; + cfg80211_debugfs_rdev_add(rdev); + nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); + if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { struct regulatory_request request; @@ -646,8 +643,6 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } - cfg80211_debugfs_rdev_add(rdev); - rdev->wiphy.registered = true; rtnl_unlock(); @@ -659,8 +654,6 @@ int wiphy_register(struct wiphy *wiphy) return res; } - nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); - return 0; } EXPORT_SYMBOL(wiphy_register); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 266766b8d80b..369fc334fdad 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -605,7 +605,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, } bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, - const u8 *buf, size_t len, u32 flags, gfp_t gfp) + const u8 *buf, size_t len, u32 flags) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); @@ -648,7 +648,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, /* Indicate the received Action frame to user space */ if (nl80211_send_mgmt(rdev, wdev, reg->nlportid, freq, sig_mbm, - buf, len, flags, gfp)) + buf, len, flags, GFP_ATOMIC)) continue; result = true; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index df7b1332a1ec..3011401f52c0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6033,7 +6033,6 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, const struct cfg80211_bss_ies *ies; void *hdr; struct nlattr *bss; - bool tsf = false; ASSERT_WDEV_LOCK(wdev); @@ -6060,18 +6059,27 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, goto nla_put_failure; rcu_read_lock(); + /* indicate whether we have probe response data or not */ + if (rcu_access_pointer(res->proberesp_ies) && + nla_put_flag(msg, NL80211_BSS_PRESP_DATA)) + goto fail_unlock_rcu; + + /* this pointer prefers to be pointed to probe response data + * but is always valid + */ ies = rcu_dereference(res->ies); if (ies) { if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) goto fail_unlock_rcu; - tsf = true; if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, ies->len, ies->data)) goto fail_unlock_rcu; } + + /* and this pointer is always (unless driver didn't know) beacon data */ ies = rcu_dereference(res->beacon_ies); - if (ies) { - if (!tsf && nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) + if (ies && ies->from_beacon) { + if (nla_put_u64(msg, NL80211_BSS_BEACON_TSF, ies->tsf)) goto fail_unlock_rcu; if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, ies->len, ies->data)) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0798c62e6085..620a4b40d466 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -884,6 +884,7 @@ struct cfg80211_bss* cfg80211_inform_bss_width(struct wiphy *wiphy, struct ieee80211_channel *rx_channel, enum nl80211_bss_scan_width scan_width, + enum cfg80211_bss_frame_type ftype, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, s32 signal, gfp_t gfp) @@ -911,21 +912,32 @@ cfg80211_inform_bss_width(struct wiphy *wiphy, tmp.pub.beacon_interval = beacon_interval; tmp.pub.capability = capability; /* - * Since we do not know here whether the IEs are from a Beacon or Probe + * If we do not know here whether the IEs are from a Beacon or Probe * Response frame, we need to pick one of the options and only use it * with the driver that does not provide the full Beacon/Probe Response * frame. Use Beacon frame pointer to avoid indicating that this should * override the IEs pointer should we have received an earlier * indication of Probe Response data. */ - ies = kmalloc(sizeof(*ies) + ielen, gfp); + ies = kzalloc(sizeof(*ies) + ielen, gfp); if (!ies) return NULL; ies->len = ielen; ies->tsf = tsf; + ies->from_beacon = false; memcpy(ies->data, ie, ielen); - rcu_assign_pointer(tmp.pub.beacon_ies, ies); + switch (ftype) { + case CFG80211_BSS_FTYPE_BEACON: + ies->from_beacon = true; + /* fall through to assign */ + case CFG80211_BSS_FTYPE_UNKNOWN: + rcu_assign_pointer(tmp.pub.beacon_ies, ies); + break; + case CFG80211_BSS_FTYPE_PRESP: + rcu_assign_pointer(tmp.pub.proberesp_ies, ies); + break; + } rcu_assign_pointer(tmp.pub.ies, ies); signal_valid = abs(rx_channel->center_freq - channel->center_freq) <= @@ -982,11 +994,12 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy, if (!channel) return NULL; - ies = kmalloc(sizeof(*ies) + ielen, gfp); + ies = kzalloc(sizeof(*ies) + ielen, gfp); if (!ies) return NULL; ies->len = ielen; ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control); memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); if (ieee80211_is_probe_resp(mgmt->frame_control))