diff options
Diffstat (limited to 'drivers')
156 files changed, 12417 insertions, 2345 deletions
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 4f78a9d39dc..be571fef185 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -89,6 +89,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0b05, 0x17d0) }, { USB_DEVICE(0x0CF3, 0x0036) }, { USB_DEVICE(0x0CF3, 0x3004) }, + { USB_DEVICE(0x0CF3, 0x3005) }, { USB_DEVICE(0x0CF3, 0x3008) }, { USB_DEVICE(0x0CF3, 0x311D) }, { USB_DEVICE(0x0CF3, 0x311E) }, @@ -137,6 +138,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311E), .driver_info = BTUSB_ATH3012 }, @@ -180,10 +182,9 @@ static int ath3k_load_firmware(struct usb_device *udev, } memcpy(send_buf, firmware->data, 20); - if ((err = usb_control_msg(udev, pipe, - USB_REQ_DFU_DNLOAD, - USB_TYPE_VENDOR, 0, 0, - send_buf, 20, USB_CTRL_SET_TIMEOUT)) < 0) { + err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR, + 0, 0, send_buf, 20, USB_CTRL_SET_TIMEOUT); + if (err < 0) { BT_ERR("Can't change to loading configuration err"); goto error; } @@ -366,7 +367,7 @@ static int ath3k_load_patch(struct usb_device *udev) } snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu", - fw_version.rom_version); + le32_to_cpu(fw_version.rom_version)); ret = request_firmware(&firmware, filename, &udev->dev); if (ret < 0) { @@ -428,7 +429,7 @@ static int ath3k_load_syscfg(struct usb_device *udev) } snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s", - fw_version.rom_version, clk_value, ".dfu"); + le32_to_cpu(fw_version.rom_version), clk_value, ".dfu"); ret = request_firmware(&firmware, filename, &udev->dev); if (ret < 0) { diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 31386998c9a..b2e7e94a677 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -131,8 +131,11 @@ static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb) BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len); - if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) - return -ENOMEM; + if (!urb) { + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + } pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep); @@ -218,8 +221,11 @@ static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb) BT_DBG("bfusb %p urb %p", data, urb); - if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC))) - return -ENOMEM; + if (!urb) { + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + } skb = bt_skb_alloc(size, GFP_ATOMIC); if (!skb) { diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 57427de864a..a9a989e5ee8 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -257,7 +257,8 @@ static void bluecard_write_wakeup(bluecard_info_t *info) ready_bit = XMIT_BUF_ONE_READY; } - if (!(skb = skb_dequeue(&(info->txq)))) + skb = skb_dequeue(&(info->txq)); + if (!skb) break; if (bt_cb(skb)->pkt_type & 0x80) { @@ -391,7 +392,8 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; - if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!info->rx_skb) { BT_ERR("Can't allocate mem for new packet"); return; } @@ -566,7 +568,8 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) /* Ericsson baud rate command */ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 }; - if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!skb) { BT_ERR("Can't allocate mem for new packet"); return -1; } diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 73d87994d02..1d82721cf9c 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -193,8 +193,8 @@ static void bt3c_write_wakeup(bt3c_info_t *info) if (!pcmcia_dev_present(info->p_dev)) break; - - if (!(skb = skb_dequeue(&(info->txq)))) { + skb = skb_dequeue(&(info->txq)); + if (!skb) { clear_bit(XMIT_SENDING, &(info->tx_state)); break; } @@ -238,7 +238,8 @@ static void bt3c_receive(bt3c_info_t *info) if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; - if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!info->rx_skb) { BT_ERR("Can't allocate mem for new packet"); return; } diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index a03ecc22a56..fb948f02eda 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -149,7 +149,8 @@ static void btuart_write_wakeup(btuart_info_t *info) if (!pcmcia_dev_present(info->p_dev)) return; - if (!(skb = skb_dequeue(&(info->txq)))) + skb = skb_dequeue(&(info->txq)); + if (!skb) break; /* Send frame */ @@ -190,7 +191,8 @@ static void btuart_receive(btuart_info_t *info) if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; - if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!info->rx_skb) { BT_ERR("Can't allocate mem for new packet"); return; } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 199b9d42489..f338b0c5a8d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -159,6 +159,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 52eed1f3565..2bd8fad1720 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -153,7 +153,8 @@ static void dtl1_write_wakeup(dtl1_info_t *info) if (!pcmcia_dev_present(info->p_dev)) return; - if (!(skb = skb_dequeue(&(info->txq)))) + skb = skb_dequeue(&(info->txq)); + if (!skb) break; /* Send frame */ @@ -215,13 +216,15 @@ static void dtl1_receive(dtl1_info_t *info) info->hdev->stat.byte_rx++; /* Allocate packet */ - if (info->rx_skb == NULL) - if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + if (info->rx_skb == NULL) { + info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!info->rx_skb) { BT_ERR("Can't allocate mem for new packet"); info->rx_state = RECV_WAIT_NSH; info->rx_count = NSHL; return; } + } *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); nsh = (nsh_t *)info->rx_skb->data; diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index 0bc87f7abd9..21cc45b34f1 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -291,7 +291,8 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) /* First of all, check for unreliable messages in the queue, since they have priority */ - if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) { + skb = skb_dequeue(&bcsp->unrel); + if (skb != NULL) { struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type); if (nskb) { kfree_skb(skb); @@ -308,16 +309,20 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING); - if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) { - struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type); - if (nskb) { - __skb_queue_tail(&bcsp->unack, skb); - mod_timer(&bcsp->tbcsp, jiffies + HZ / 4); - spin_unlock_irqrestore(&bcsp->unack.lock, flags); - return nskb; - } else { - skb_queue_head(&bcsp->rel, skb); - BT_ERR("Could not dequeue pkt because alloc_skb failed"); + if (bcsp->unack.qlen < BCSP_TXWINSIZE) { + skb = skb_dequeue(&bcsp->rel); + if (skb != NULL) { + struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, + bt_cb(skb)->pkt_type); + if (nskb) { + __skb_queue_tail(&bcsp->unack, skb); + mod_timer(&bcsp->tbcsp, jiffies + HZ / 4); + spin_unlock_irqrestore(&bcsp->unack.lock, flags); + return nskb; + } else { + skb_queue_head(&bcsp->rel, skb); + BT_ERR("Could not dequeue pkt because alloc_skb failed"); + } } } @@ -715,6 +720,9 @@ static int bcsp_open(struct hci_uart *hu) static int bcsp_close(struct hci_uart *hu) { struct bcsp_struct *bcsp = hu->priv; + + del_timer_sync(&bcsp->tbcsp); + hu->priv = NULL; BT_DBG("hu %p", hu); @@ -722,7 +730,6 @@ static int bcsp_close(struct hci_uart *hu) skb_queue_purge(&bcsp->unack); skb_queue_purge(&bcsp->rel); skb_queue_purge(&bcsp->unrel); - del_timer(&bcsp->tbcsp); kfree(bcsp); return 0; diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index f6f49745056..04680ead927 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -206,12 +206,12 @@ static int h5_close(struct hci_uart *hu) { struct h5 *h5 = hu->priv; + del_timer_sync(&h5->timer); + skb_queue_purge(&h5->unack); skb_queue_purge(&h5->rel); skb_queue_purge(&h5->unrel); - del_timer(&h5->timer); - kfree(h5); return 0; @@ -673,7 +673,8 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) return h5_prepare_pkt(hu, HCI_3WIRE_LINK_PKT, wakeup_req, 2); } - if ((skb = skb_dequeue(&h5->unrel)) != NULL) { + skb = skb_dequeue(&h5->unrel); + if (skb != NULL) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { @@ -690,7 +691,8 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) if (h5->unack.qlen >= h5->tx_win) goto unlock; - if ((skb = skb_dequeue(&h5->rel)) != NULL) { + skb = skb_dequeue(&h5->rel); + if (skb != NULL) { nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, skb->data, skb->len); if (nskb) { diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 6e06f6f6915..f1fbf4f1e5b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -271,7 +271,8 @@ static int hci_uart_tty_open(struct tty_struct *tty) if (tty->ops->write == NULL) return -EOPNOTSUPP; - if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) { + hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL); + if (!hu) { BT_ERR("Can't allocate control structure"); return -ENFILE; } @@ -569,7 +570,8 @@ static int __init hci_uart_init(void) hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup; hci_uart_ldisc.owner = THIS_MODULE; - if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { + err = tty_register_ldisc(N_HCI, &hci_uart_ldisc); + if (err) { BT_ERR("HCI line discipline registration failed. (%d)", err); return err; } @@ -614,7 +616,8 @@ static void __exit hci_uart_exit(void) #endif /* Release tty registration of line discipline */ - if ((err = tty_unregister_ldisc(N_HCI))) + err = tty_unregister_ldisc(N_HCI); + if (err) BT_ERR("Can't unregister HCI line discipline (%d)", err); } diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d1fab435f5a..b2137e8f7ca 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -116,7 +116,7 @@ config AT76C50X_USB config AIRO_CS tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" - depends on PCMCIA && (BROKEN || !M32R) + depends on CFG80211 && PCMCIA && (BROKEN || !M32R) select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV @@ -281,5 +281,6 @@ source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/cw1200/Kconfig" +source "drivers/net/wireless/rsi/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 0fab227025b..0c889168671 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/ obj-$(CONFIG_CW1200) += cw1200/ +obj-$(CONFIG_RSI_91X) += rsi/ diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index d239acc2612..a889fd66fc6 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -56,6 +56,15 @@ enum ath_device_state { ATH_HW_INITIALIZED, }; +enum ath_op_flags { + ATH_OP_INVALID, + ATH_OP_BEACONS, + ATH_OP_ANI_RUN, + ATH_OP_PRIM_STA_VIF, + ATH_OP_HW_RESET, + ATH_OP_SCANNING, +}; + enum ath_bus_type { ATH_PCI, ATH_AHB, @@ -130,6 +139,7 @@ struct ath_common { struct ieee80211_hw *hw; int debug_mask; enum ath_device_state state; + unsigned long op_flags; struct ath_ani ani; diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index d44d618b05f..a79499c8235 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -266,12 +266,12 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, * ath10k_ce_sendlist_send. * The caller takes responsibility for any needed locking. */ -static int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, - void *per_transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags) +int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, + void *per_transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) { struct ath10k *ar = ce_state->ar; struct ath10k_ce_ring *src_ring = ce_state->src_ring; @@ -1067,9 +1067,9 @@ struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar, * * For the lack of a better place do the check here. */ - BUILD_BUG_ON(TARGET_NUM_MSDU_DESC > + BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); - BUILD_BUG_ON(TARGET_10X_NUM_MSDU_DESC > + BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); ret = ath10k_pci_wake(ar); diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 67dbde6a5c7..8eb7f99ed99 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -23,7 +23,7 @@ /* Maximum number of Copy Engine's supported */ #define CE_COUNT_MAX 8 -#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048 +#define CE_HTT_H2T_MSG_SRC_NENTRIES 4096 /* Descriptor rings must be aligned to this boundary */ #define CE_DESC_RING_ALIGN 8 @@ -152,6 +152,13 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, unsigned int transfer_id, unsigned int flags); +int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, + void *per_transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags); + void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 1fc26fe057e..0e71979d837 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -62,16 +62,13 @@ struct ath10k; struct ath10k_skb_cb { dma_addr_t paddr; - bool is_mapped; - bool is_aborted; u8 vdev_id; struct { u8 tid; bool is_offchan; - - u8 frag_len; - u8 pad_len; + struct ath10k_htt_txbuf *txbuf; + u32 txbuf_paddr; } __packed htt; struct { @@ -87,32 +84,6 @@ static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; } -static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb) -{ - if (ATH10K_SKB_CB(skb)->is_mapped) - return -EINVAL; - - ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len, - DMA_TO_DEVICE); - - if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr))) - return -EIO; - - ATH10K_SKB_CB(skb)->is_mapped = true; - return 0; -} - -static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb) -{ - if (!ATH10K_SKB_CB(skb)->is_mapped) - return -EINVAL; - - dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len, - DMA_TO_DEVICE); - ATH10K_SKB_CB(skb)->is_mapped = false; - return 0; -} - static inline u32 host_interest_item_address(u32 item_offset) { return QCA988X_HOST_INTEREST_ADDRESS + item_offset; @@ -288,6 +259,7 @@ struct ath10k_vif { u8 fixed_rate; u8 fixed_nss; + u8 force_sgi; }; struct ath10k_vif_iter { diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index dcdea68bcc0..2ac7beacddc 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -21,6 +21,14 @@ #include <linux/kernel.h> #include "core.h" +struct ath10k_hif_sg_item { + u16 transfer_id; + void *transfer_context; /* NULL = tx completion callback not called */ + void *vaddr; /* for debugging mostly */ + u32 paddr; + u16 len; +}; + struct ath10k_hif_cb { int (*tx_completion)(struct ath10k *ar, struct sk_buff *wbuf, @@ -31,11 +39,9 @@ struct ath10k_hif_cb { }; struct ath10k_hif_ops { - /* Send the head of a buffer to HIF for transmission to the target. */ - int (*send_head)(struct ath10k *ar, u8 pipe_id, - unsigned int transfer_id, - unsigned int nbytes, - struct sk_buff *buf); + /* send a scatter-gather list to the target */ + int (*tx_sg)(struct ath10k *ar, u8 pipe_id, + struct ath10k_hif_sg_item *items, int n_items); /* * API to handle HIF-specific BMI message exchanges, this API is @@ -86,12 +92,11 @@ struct ath10k_hif_ops { }; -static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id, - unsigned int transfer_id, - unsigned int nbytes, - struct sk_buff *buf) +static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id, + struct ath10k_hif_sg_item *items, + int n_items) { - return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf); + return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items); } static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar, diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index edc57ab505c..7f1bccd3597 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -63,7 +63,9 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc, struct sk_buff *skb) { - ath10k_skb_unmap(htc->ar->dev, skb); + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + + dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); skb_pull(skb, sizeof(struct ath10k_htc_hdr)); } @@ -122,6 +124,9 @@ int ath10k_htc_send(struct ath10k_htc *htc, struct sk_buff *skb) { 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; + struct device *dev = htc->ar->dev; int credits = 0; int ret; @@ -157,19 +162,25 @@ int ath10k_htc_send(struct ath10k_htc *htc, ath10k_htc_prepare_tx_skb(ep, skb); - ret = ath10k_skb_map(htc->ar->dev, skb); + skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, skb_cb->paddr); if (ret) goto err_credits; - ret = ath10k_hif_send_head(htc->ar, ep->ul_pipe_id, ep->eid, - skb->len, skb); + sg_item.transfer_id = ep->eid; + sg_item.transfer_context = skb; + sg_item.vaddr = skb->data; + sg_item.paddr = skb_cb->paddr; + sg_item.len = skb->len; + + ret = ath10k_hif_tx_sg(htc->ar, ep->ul_pipe_id, &sg_item, 1); if (ret) goto err_unmap; return 0; err_unmap: - ath10k_skb_unmap(htc->ar->dev, skb); + dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); err_credits: if (ep->tx_credit_flow_enabled) { spin_lock_bh(&htc->tx_lock); @@ -191,10 +202,8 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar, struct ath10k_htc *htc = &ar->htc; struct ath10k_htc_ep *ep = &htc->endpoint[eid]; - if (!skb) { - ath10k_warn("invalid sk_buff completion - NULL pointer. firmware crashed?\n"); + if (WARN_ON_ONCE(!skb)) return 0; - } ath10k_htc_notify_tx_completion(ep, skb); /* the skb now belongs to the completion handler */ diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index b93ae355bc0..654867fc1ae 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -20,6 +20,7 @@ #include <linux/bug.h> #include <linux/interrupt.h> +#include <linux/dmapool.h> #include "htc.h" #include "rx_desc.h" @@ -1181,11 +1182,20 @@ struct htt_rx_info { u32 info1; u32 info2; } rate; + + u32 tsf; bool fcs_err; bool amsdu_more; bool mic_err; }; +struct ath10k_htt_txbuf { + struct htt_data_tx_desc_frag frags[2]; + struct ath10k_htc_hdr htc_hdr; + struct htt_cmd_hdr cmd_hdr; + struct htt_data_tx_desc cmd_tx; +} __packed; + struct ath10k_htt { struct ath10k *ar; enum ath10k_htc_ep_id eid; @@ -1267,11 +1277,18 @@ struct ath10k_htt { struct sk_buff **pending_tx; unsigned long *used_msdu_ids; /* bitmap */ wait_queue_head_t empty_tx_wq; + struct dma_pool *tx_pool; /* set if host-fw communication goes haywire * used to avoid further failures */ bool rx_confused; struct tasklet_struct rx_replenish_task; + + /* This is used to group tx/rx completions separately and process them + * in batches to reduce cache stalls */ + struct tasklet_struct txrx_compl_task; + struct sk_buff_head tx_compl_q; + struct sk_buff_head rx_compl_q; }; #define RX_HTT_HDR_STATUS_LEN 64 @@ -1343,4 +1360,5 @@ int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt); void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *); + #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 4767c24bf81..cdcbe2de95f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -43,7 +43,7 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb); - +static void ath10k_htt_txrx_compl_task(unsigned long ptr); static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt) { @@ -225,18 +225,16 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg) ath10k_htt_rx_msdu_buff_replenish(htt); } -static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt) -{ - return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) - - htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask; -} - void ath10k_htt_rx_detach(struct ath10k_htt *htt) { int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; del_timer_sync(&htt->rx_ring.refill_retry_timer); tasklet_kill(&htt->rx_replenish_task); + tasklet_kill(&htt->txrx_compl_task); + + skb_queue_purge(&htt->tx_compl_q); + skb_queue_purge(&htt->rx_compl_q); while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { struct sk_buff *skb = @@ -270,10 +268,12 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) int idx; struct sk_buff *msdu; - spin_lock_bh(&htt->rx_ring.lock); + lockdep_assert_held(&htt->rx_ring.lock); - if (ath10k_htt_rx_ring_elems(htt) == 0) - ath10k_warn("htt rx ring is empty!\n"); + if (htt->rx_ring.fill_cnt == 0) { + ath10k_warn("tried to pop sk_buff from an empty rx ring\n"); + return NULL; + } idx = htt->rx_ring.sw_rd_idx.msdu_payld; msdu = htt->rx_ring.netbufs_ring[idx]; @@ -283,7 +283,6 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) htt->rx_ring.sw_rd_idx.msdu_payld = idx; htt->rx_ring.fill_cnt--; - spin_unlock_bh(&htt->rx_ring.lock); return msdu; } @@ -307,8 +306,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, struct sk_buff *msdu; struct htt_rx_desc *rx_desc; - if (ath10k_htt_rx_ring_elems(htt) == 0) - ath10k_warn("htt rx ring is empty!\n"); + lockdep_assert_held(&htt->rx_ring.lock); if (htt->rx_confused) { ath10k_warn("htt is confused. refusing rx\n"); @@ -400,6 +398,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0), RX_MSDU_START_INFO0_MSDU_LENGTH); msdu_chained = rx_desc->frag_info.ring2_more_count; + msdu_chaining = msdu_chained; if (msdu_len_invalid) msdu_len = 0; @@ -427,7 +426,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu->next = next; msdu = next; - msdu_chaining = 1; } last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & @@ -529,6 +527,12 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task, (unsigned long)htt); + skb_queue_head_init(&htt->tx_compl_q); + skb_queue_head_init(&htt->rx_compl_q); + + 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", htt->rx_ring.size, htt->rx_ring.fill_level); return 0; @@ -632,6 +636,12 @@ struct amsdu_subframe_hdr { __be16 len; } __packed; +static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr) +{ + /* nwifi header is padded to 4 bytes. this fixes 4addr rx */ + return round_up(ieee80211_hdrlen(hdr->frame_control), 4); +} + static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, struct htt_rx_info *info) { @@ -681,7 +691,7 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, case RX_MSDU_DECAP_NATIVE_WIFI: /* pull decapped header and copy DA */ hdr = (struct ieee80211_hdr *)skb->data; - hdr_len = ieee80211_hdrlen(hdr->frame_control); + hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr); memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN); skb_pull(skb, hdr_len); @@ -768,7 +778,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) case RX_MSDU_DECAP_NATIVE_WIFI: /* Pull decapped header */ hdr = (struct ieee80211_hdr *)skb->data; - hdr_len = ieee80211_hdrlen(hdr->frame_control); + hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr); skb_pull(skb, hdr_len); /* Push original header */ @@ -846,6 +856,20 @@ static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb) return false; } +static bool ath10k_htt_rx_is_mgmt(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_MGMT_TYPE) + return true; + + return false; +} + static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) { struct htt_rx_desc *rxd; @@ -877,6 +901,57 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) return CHECKSUM_UNNECESSARY; } +static int ath10k_unchain_msdu(struct sk_buff *msdu_head) +{ + struct sk_buff *next = msdu_head->next; + struct sk_buff *to_free = next; + int space; + int total_len = 0; + + /* TODO: Might could optimize this by using + * skb_try_coalesce or similar method to + * decrease copying, or maybe get mac80211 to + * provide a way to just receive a list of + * skb? + */ + + msdu_head->next = NULL; + + /* Allocate total length all at once. */ + while (next) { + total_len += next->len; + next = next->next; + } + + space = total_len - skb_tailroom(msdu_head); + if ((space > 0) && + (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) { + /* TODO: bump some rx-oom error stat */ + /* put it back together so we can free the + * whole list at once. + */ + msdu_head->next = to_free; + return -1; + } + + /* Walk list again, copying contents into + * msdu_head + */ + next = to_free; + while (next) { + skb_copy_from_linear_data(next, skb_put(msdu_head, next->len), + next->len); + next = next->next; + } + + /* If here, we have consolidated skb. Free the + * fragments and pass the main skb on up the + * stack. + */ + ath10k_htt_rx_free_msdu_chain(to_free); + return 0; +} + static void ath10k_htt_rx_handler(struct ath10k_htt *htt, struct htt_rx_indication *rx) { @@ -888,6 +963,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, u8 *fw_desc; int i, j; + lockdep_assert_held(&htt->rx_ring.lock); + memset(&info, 0, sizeof(info)); fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes); @@ -940,7 +1017,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, status = info.status; /* Skip mgmt frames while we handle this in WMI */ - if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) { + if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL || + ath10k_htt_rx_is_mgmt(msdu_head)) { ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n"); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; @@ -964,10 +1042,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, continue; } - /* FIXME: we do not support chaining yet. - * this needs investigation */ - if (msdu_chaining) { - ath10k_warn("htt rx msdu_chaining is true\n"); + if (msdu_chaining && + (ath10k_unchain_msdu(msdu_head) < 0)) { ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } @@ -990,6 +1066,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, info.rate.info0 = rx->ppdu.info0; info.rate.info1 = __le32_to_cpu(rx->ppdu.info1); info.rate.info2 = __le32_to_cpu(rx->ppdu.info2); + info.tsf = __le32_to_cpu(rx->ppdu.tsf); hdr = ath10k_htt_rx_skb_get_hdr(msdu_head); @@ -1023,8 +1100,11 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, msdu_head = NULL; msdu_tail = NULL; + + spin_lock_bh(&htt->rx_ring.lock); msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, &msdu_head, &msdu_tail); + spin_unlock_bh(&htt->rx_ring.lock); ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); @@ -1116,6 +1196,45 @@ end: } } +static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar, + struct sk_buff *skb) +{ + struct ath10k_htt *htt = &ar->htt; + struct htt_resp *resp = (struct htt_resp *)skb->data; + struct htt_tx_done tx_done = {}; + int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS); + __le16 msdu_id; + int i; + + lockdep_assert_held(&htt->tx_lock); + + switch (status) { + case HTT_DATA_TX_STATUS_NO_ACK: + tx_done.no_ack = true; + break; + case HTT_DATA_TX_STATUS_OK: + break; + case HTT_DATA_TX_STATUS_DISCARD: + case HTT_DATA_TX_STATUS_POSTPONE: + case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL: + tx_done.discard = true; + break; + default: + ath10k_warn("unhandled tx completion status %d\n", status); + tx_done.discard = true; + break; + } + + ath10k_dbg(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++) { + msdu_id = resp->data_tx_completion.msdus[i]; + tx_done.msdu_id = __le16_to_cpu(msdu_id); + ath10k_txrx_tx_unref(htt, &tx_done); + } +} + void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_htt *htt = &ar->htt; @@ -1134,10 +1253,12 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) complete(&htt->target_version_received); break; } - case HTT_T2H_MSG_TYPE_RX_IND: { - ath10k_htt_rx_handler(htt, &resp->rx_ind); - break; - } + case HTT_T2H_MSG_TYPE_RX_IND: + spin_lock_bh(&htt->rx_ring.lock); + __skb_queue_tail(&htt->rx_compl_q, skb); + spin_unlock_bh(&htt->rx_ring.lock); + tasklet_schedule(&htt->txrx_compl_task); + return; case HTT_T2H_MSG_TYPE_PEER_MAP: { struct htt_peer_map_event ev = { .vdev_id = resp->peer_map.vdev_id, @@ -1172,44 +1293,17 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } + spin_lock_bh(&htt->tx_lock); ath10k_txrx_tx_unref(htt, &tx_done); + spin_unlock_bh(&htt->tx_lock); break; } - case HTT_T2H_MSG_TYPE_TX_COMPL_IND: { - struct htt_tx_done tx_done = {}; - int status = MS(resp->data_tx_completion.flags, - HTT_DATA_TX_STATUS); - __le16 msdu_id; - int i; - - switch (status) { - case HTT_DATA_TX_STATUS_NO_ACK: - tx_done.no_ack = true; - break; - case HTT_DATA_TX_STATUS_OK: - break; - case HTT_DATA_TX_STATUS_DISCARD: - case HTT_DATA_TX_STATUS_POSTPONE: - case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL: - tx_done.discard = true; - break; - default: - ath10k_warn("unhandled tx completion status %d\n", - status); - tx_done.discard = true; - break; - } - - ath10k_dbg(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++) { - msdu_id = resp->data_tx_completion.msdus[i]; - tx_done.msdu_id = __le16_to_cpu(msdu_id); - ath10k_txrx_tx_unref(htt, &tx_done); - } - break; - } + case HTT_T2H_MSG_TYPE_TX_COMPL_IND: + spin_lock_bh(&htt->tx_lock); + __skb_queue_tail(&htt->tx_compl_q, skb); + spin_unlock_bh(&htt->tx_lock); + tasklet_schedule(&htt->txrx_compl_task); + return; case HTT_T2H_MSG_TYPE_SEC_IND: { struct ath10k *ar = htt->ar; struct htt_security_indication *ev = &resp->security_indication; @@ -1249,3 +1343,25 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) /* Free the indication buffer */ dev_kfree_skb_any(skb); } + +static void ath10k_htt_txrx_compl_task(unsigned long ptr) +{ + struct ath10k_htt *htt = (struct ath10k_htt *)ptr; + struct htt_resp *resp; + struct sk_buff *skb; + + spin_lock_bh(&htt->tx_lock); + while ((skb = __skb_dequeue(&htt->tx_compl_q))) { + ath10k_htt_rx_frm_tx_compl(htt->ar, skb); + dev_kfree_skb_any(skb); + } + spin_unlock_bh(&htt->tx_lock); + + spin_lock_bh(&htt->rx_ring.lock); + while ((skb = __skb_dequeue(&htt->rx_compl_q))) { + resp = (struct htt_resp *)skb->data; + ath10k_htt_rx_handler(htt, &resp->rx_ind); + dev_kfree_skb_any(skb); + } + spin_unlock_bh(&htt->rx_ring.lock); +} diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index acaa046dc93..7a3e2e40dd5 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -109,6 +109,14 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) return -ENOMEM; } + htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev, + sizeof(struct ath10k_htt_txbuf), 4, 0); + if (!htt->tx_pool) { + kfree(htt->used_msdu_ids); + kfree(htt->pending_tx); + return -ENOMEM; + } + return 0; } @@ -117,9 +125,7 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) struct htt_tx_done tx_done = {0}; int msdu_id; - /* No locks needed. Called after communication with the device has - * been stopped. */ - + spin_lock_bh(&htt->tx_lock); for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) { if (!test_bit(msdu_id, htt->used_msdu_ids)) continue; @@ -132,6 +138,7 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) ath10k_txrx_tx_unref(htt, &tx_done); } + spin_unlock_bh(&htt->tx_lock); } void ath10k_htt_tx_detach(struct ath10k_htt *htt) @@ -139,6 +146,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt) ath10k_htt_tx_cleanup_pending(htt); kfree(htt->pending_tx); kfree(htt->used_msdu_ids); + dma_pool_destroy(htt->tx_pool); return; } @@ -334,7 +342,9 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) goto err_free_msdu_id; } - res = ath10k_skb_map(dev, msdu); + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, + DMA_TO_DEVICE); + res = dma_mapping_error(dev, skb_cb->paddr); if (res) goto err_free_txdesc; @@ -348,8 +358,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) memcpy(cmd->mgmt_tx.hdr, msdu->data, min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN)); - skb_cb->htt.frag_len = 0; - skb_cb->htt.pad_len = 0; + skb_cb->htt.txbuf = NULL; res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) @@ -358,7 +367,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) return 0; err_unmap_msdu: - ath10k_skb_unmap(dev, msdu); + dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); err_free_txdesc: dev_kfree_skb_any(txdesc); err_free_msdu_id: @@ -375,19 +384,19 @@ err: int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { struct device *dev = htt->ar->dev; - struct htt_cmd *cmd; - struct htt_data_tx_desc_frag *tx_frags; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); - struct sk_buff *txdesc = NULL; - bool use_frags; - u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id; - u8 tid; - int prefetch_len, desc_len; - int msdu_id = -1; + struct ath10k_hif_sg_item sg_items[2]; + struct htt_data_tx_desc_frag *frags; + u8 vdev_id = skb_cb->vdev_id; + u8 tid = skb_cb->htt.tid; + int prefetch_len; int res; - u8 flags0; - u16 flags1; + u8 flags0 = 0; + u16 msdu_id, flags1 = 0; + dma_addr_t paddr; + u32 frags_paddr; + bool use_frags; res = ath10k_htt_tx_inc_pending(htt); if (res) @@ -406,114 +415,120 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) prefetch_len = min(htt->prefetch_len, msdu->len); prefetch_len = roundup(prefetch_len, 4); - desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len; - - txdesc = ath10k_htc_alloc_skb(desc_len); - if (!txdesc) { - res = -ENOMEM; - goto err_free_msdu_id; - } - /* Since HTT 3.0 there is no separate mgmt tx command. However in case * of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx * fragment list host driver specifies directly frame pointer. */ use_frags = htt->target_version_major < 3 || !ieee80211_is_mgmt(hdr->frame_control); - if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) { - ath10k_warn("htt alignment check failed. dropping packet.\n"); - res = -EIO; - goto err_free_txdesc; - } + skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC, + &paddr); + if (!skb_cb->htt.txbuf) + goto err_free_msdu_id; + skb_cb->htt.txbuf_paddr = paddr; - if (use_frags) { - skb_cb->htt.frag_len = sizeof(*tx_frags) * 2; - skb_cb->htt.pad_len = (unsigned long)msdu->data - - round_down((unsigned long)msdu->data, 4); + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, + DMA_TO_DEVICE); + res = dma_mapping_error(dev, skb_cb->paddr); + if (res) + goto err_free_txbuf; - skb_push(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len); - } else { - skb_cb->htt.frag_len = 0; - skb_cb->htt.pad_len = 0; - } + if (likely(use_frags)) { + frags = skb_cb->htt.txbuf->frags; - res = ath10k_skb_map(dev, msdu); - if (res) - goto err_pull_txfrag; - - if (use_frags) { - dma_sync_single_for_cpu(dev, skb_cb->paddr, msdu->len, - DMA_TO_DEVICE); - - /* tx fragment list must be terminated with zero-entry */ - tx_frags = (struct htt_data_tx_desc_frag *)msdu->data; - tx_frags[0].paddr = __cpu_to_le32(skb_cb->paddr + - skb_cb->htt.frag_len + - skb_cb->htt.pad_len); - tx_frags[0].len = __cpu_to_le32(msdu->len - - skb_cb->htt.frag_len - - skb_cb->htt.pad_len); - tx_frags[1].paddr = __cpu_to_le32(0); - tx_frags[1].len = __cpu_to_le32(0); - - dma_sync_single_for_device(dev, skb_cb->paddr, msdu->len, - DMA_TO_DEVICE); - } + frags[0].paddr = __cpu_to_le32(skb_cb->paddr); + frags[0].len = __cpu_to_le32(msdu->len); + frags[1].paddr = 0; + frags[1].len = 0; - ath10k_dbg(ATH10K_DBG_HTT, "tx-msdu 0x%llx\n", - (unsigned long long) ATH10K_SKB_CB(msdu)->paddr); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "tx-msdu: ", - msdu->data, msdu->len); + flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); - skb_put(txdesc, desc_len); - cmd = (struct htt_cmd *)txdesc->data; + frags_paddr = skb_cb->htt.txbuf_paddr; + } else { + flags0 |= SM(ATH10K_HW_TXRX_MGMT, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); - tid = ATH10K_SKB_CB(msdu)->htt.tid; + frags_paddr = skb_cb->paddr; + } - ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid); + /* Normally all commands go through HTC which manages tx credits for + * each endpoint and notifies when tx is completed. + * + * HTT endpoint is creditless so there's no need to care about HTC + * flags. In that case it is trivial to fill the HTC header here. + * + * MSDU transmission is considered completed upon HTT event. This + * implies no relevant resources can be freed until after the event is + * received. That's why HTC tx completion handler itself is ignored by + * setting NULL to transfer_context for all sg items. + * + * There is simply no point in pushing HTT TX_FRM through HTC tx path + * as it's a waste of resources. By bypassing HTC it is possible to + * avoid extra memory allocations, compress data structures and thus + * improve performance. */ + + skb_cb->htt.txbuf->htc_hdr.eid = htt->eid; + skb_cb->htt.txbuf->htc_hdr.len = __cpu_to_le16( + sizeof(skb_cb->htt.txbuf->cmd_hdr) + + sizeof(skb_cb->htt.txbuf->cmd_tx) + + prefetch_len); + skb_cb->htt.txbuf->htc_hdr.flags = 0; - flags0 = 0; if (!ieee80211_has_protected(hdr->frame_control)) flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; - flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; - if (use_frags) - flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, - HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); - else - flags0 |= SM(ATH10K_HW_TXRX_MGMT, - HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; - flags1 = 0; flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; - cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; - cmd->data_tx.flags0 = flags0; - cmd->data_tx.flags1 = __cpu_to_le16(flags1); - cmd->data_tx.len = __cpu_to_le16(msdu->len - - skb_cb->htt.frag_len - - skb_cb->htt.pad_len); - cmd->data_tx.id = __cpu_to_le16(msdu_id); - cmd->data_tx.frags_paddr = __cpu_to_le32(skb_cb->paddr); - cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); - - memcpy(cmd->data_tx.prefetch, hdr, prefetch_len); + skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; + skb_cb->htt.txbuf->cmd_tx.flags0 = flags0; + skb_cb->htt.txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1); + skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len); + skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id); + 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, + "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: ", + msdu->data, msdu->len); - res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); + sg_items[0].transfer_id = 0; + sg_items[0].transfer_context = NULL; + sg_items[0].vaddr = &skb_cb->htt.txbuf->htc_hdr; + sg_items[0].paddr = skb_cb->htt.txbuf_paddr + + sizeof(skb_cb->htt.txbuf->frags); + sg_items[0].len = sizeof(skb_cb->htt.txbuf->htc_hdr) + + sizeof(skb_cb->htt.txbuf->cmd_hdr) + + sizeof(skb_cb->htt.txbuf->cmd_tx); + + sg_items[1].transfer_id = 0; + sg_items[1].transfer_context = NULL; + sg_items[1].vaddr = msdu->data; + sg_items[1].paddr = skb_cb->paddr; + sg_items[1].len = prefetch_len; + + res = ath10k_hif_tx_sg(htt->ar, + htt->ar->htc.endpoint[htt->eid].ul_pipe_id, + sg_items, ARRAY_SIZE(sg_items)); if (res) goto err_unmap_msdu; return 0; err_unmap_msdu: - ath10k_skb_unmap(dev, msdu); -err_pull_txfrag: - skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len); -err_free_txdesc: - dev_kfree_skb_any(txdesc); + dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); +err_free_txbuf: + dma_pool_free(htt->tx_pool, + skb_cb->htt.txbuf, + skb_cb->htt.txbuf_paddr); err_free_msdu_id: spin_lock_bh(&htt->tx_lock); htt->pending_tx[msdu_id] = NULL; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index e17f5d732b5..511a2f81e7a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -323,13 +323,15 @@ 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: %i\n", ret); + ath10k_warn("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: %i\n", ret); + ath10k_warn("Failed to wait for created wmi peer %pM on vdev %i: %i\n", + addr, vdev_id, ret); return ret; } spin_lock_bh(&ar->data_lock); @@ -349,7 +351,8 @@ 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: %d\n", ret); + ath10k_warn("Failed to set kickout threshold on vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -357,8 +360,8 @@ 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 : %d\n", - ret); + ath10k_warn("Failed to set keepalive minimum idle time on vdev %i : %d\n", + arvif->vdev_id, ret); return ret; } @@ -366,8 +369,8 @@ 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: %d\n", - ret); + ath10k_warn("Failed to set keepalive maximum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -375,8 +378,8 @@ 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: %d\n", - ret); + ath10k_warn("Failed to set keepalive maximum unresponsive time on vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -529,13 +532,15 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { - ath10k_warn("WMI vdev start failed: ret %d\n", ret); + ath10k_warn("WMI vdev %i start failed: ret %d\n", + arg.vdev_id, ret); return ret; } ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn("vdev setup failed %d\n", ret); + ath10k_warn("vdev %i setup failed %d\n", + arg.vdev_id, ret); return ret; } @@ -553,13 +558,15 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); if (ret) { - ath10k_warn("WMI vdev stop failed: ret %d\n", ret); + ath10k_warn("WMI vdev %i stop failed: ret %d\n", + arvif->vdev_id, ret); return ret; } ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn("vdev setup failed %d\n", ret); + ath10k_warn("vdev %i setup sync failed %d\n", + arvif->vdev_id, ret); return ret; } @@ -597,19 +604,22 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { - ath10k_warn("Monitor vdev start failed: ret %d\n", ret); + ath10k_warn("Monitor vdev %i start failed: ret %d\n", + vdev_id, ret); return ret; } ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn("Monitor vdev setup failed %d\n", ret); + ath10k_warn("Monitor vdev %i setup failed %d\n", + vdev_id, ret); return ret; } ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); if (ret) { - ath10k_warn("Monitor vdev up failed: %d\n", ret); + ath10k_warn("Monitor vdev %i up failed: %d\n", + vdev_id, ret); goto vdev_stop; } @@ -621,7 +631,8 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) vdev_stop: ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("Monitor vdev stop failed: %d\n", ret); + ath10k_warn("Monitor vdev %i stop failed: %d\n", + ar->monitor_vdev_id, ret); return ret; } @@ -644,15 +655,18 @@ static int ath10k_monitor_stop(struct ath10k *ar) ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("Monitor vdev down failed: %d\n", ret); + ath10k_warn("Monitor vdev %i down failed: %d\n", + ar->monitor_vdev_id, ret); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("Monitor vdev stop failed: %d\n", ret); + ath10k_warn("Monitor vdev %i stop failed: %d\n", + ar->monitor_vdev_id, ret); ret = ath10k_vdev_setup_sync(ar); if (ret) - ath10k_warn("Monitor_down sync failed: %d\n", ret); + ath10k_warn("Monitor_down sync failed, vdev %i: %d\n", + ar->monitor_vdev_id, ret); ar->monitor_enabled = false; return ret; @@ -682,7 +696,8 @@ static int ath10k_monitor_create(struct ath10k *ar) WMI_VDEV_TYPE_MONITOR, 0, ar->mac_addr); if (ret) { - ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret); + ath10k_warn("WMI vdev %i monitor create failed: ret %d\n", + ar->monitor_vdev_id, ret); goto vdev_fail; } @@ -711,7 +726,8 @@ static int ath10k_monitor_destroy(struct ath10k *ar) ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id); if (ret) { - ath10k_warn("WMI vdev monitor delete failed: %d\n", ret); + ath10k_warn("WMI vdev %i monitor delete failed: %d\n", + ar->monitor_vdev_id, ret); return ret; } @@ -839,7 +855,9 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, spin_lock_bh(&arvif->ar->data_lock); if (arvif->beacon) { - ath10k_skb_unmap(arvif->ar->dev, arvif->beacon); + dma_unmap_single(arvif->ar->dev, + ATH10K_SKB_CB(arvif->beacon)->paddr, + arvif->beacon->len, DMA_TO_DEVICE); dev_kfree_skb_any(arvif->beacon); arvif->beacon = NULL; @@ -862,8 +880,8 @@ 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\n", - arvif->vdev_id); + ath10k_warn("Failed to bring up vdev %d: %i\n", + arvif->vdev_id, ret); ath10k_vdev_stop(arvif); return; } @@ -943,8 +961,8 @@ 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\n", - arvif->vdev_id); + ath10k_warn("Failed to set inactivity time for vdev %d: %i\n", + arvif->vdev_id, ret); return ret; } } else { @@ -1196,8 +1214,8 @@ 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: %d\n", - ret); + ath10k_warn("failed to set ap ps peer param uapsd for vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -1206,8 +1224,8 @@ 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: %d\n", - ret); + ath10k_warn("failed to set ap ps peer param max sp for vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -1218,8 +1236,8 @@ 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: %d\n", - ret); + ath10k_warn("failed to set ap ps peer param ageout time for vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } } @@ -1411,8 +1429,8 @@ 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 %pM\n", - bss_conf->bssid); + ath10k_warn("Failed to find station entry for %pM, vdev %i\n", + bss_conf->bssid, arvif->vdev_id); rcu_read_unlock(); return; } @@ -1424,8 +1442,8 @@ 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("Peer assoc prepare failed for %pM\n: %d", - bss_conf->bssid, ret); + ath10k_warn("Peer assoc prepare failed for %pM vdev %i\n: %d", + bss_conf->bssid, arvif->vdev_id, ret); rcu_read_unlock(); return; } @@ -1434,14 +1452,15 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) { - ath10k_warn("Peer assoc failed for %pM\n: %d", - bss_conf->bssid, ret); + ath10k_warn("Peer assoc failed for %pM vdev %i\n: %d", + 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: %d\n", ret); + ath10k_warn("failed to setup peer SMPS for vdev %i: %d\n", + arvif->vdev_id, ret); return; } @@ -1514,34 +1533,35 @@ 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("WMI peer assoc prepare failed for %pM\n", - sta->addr); + ath10k_warn("WMI peer assoc prepare failed for %pM vdev %i: %i\n", + sta->addr, arvif->vdev_id, ret); return ret; } ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) { - ath10k_warn("Peer assoc failed for STA %pM\n: %d", - sta->addr, ret); + ath10k_warn("Peer assoc failed 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: %d\n", ret); + ath10k_warn("failed to setup peer SMPS for vdev: %d\n", ret); return ret; } ret = ath10k_install_peer_wep_keys(arvif, sta->addr); if (ret) { - ath10k_warn("could not install peer wep keys (%d)\n", ret); + ath10k_warn("could not 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("could not set qos params for STA %pM, %d\n", - sta->addr, ret); + ath10k_warn("could not set qos params for STA %pM for vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); return ret; } @@ -1557,7 +1577,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, ret = ath10k_clear_peer_keys(arvif, sta->addr); if (ret) { - ath10k_warn("could not clear all peer wep keys (%d)\n", ret); + ath10k_warn("could not clear all peer wep keys for vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -2524,7 +2545,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, vif->addr); if (ret) { - ath10k_warn("WMI vdev create failed: ret %d\n", ret); + ath10k_warn("WMI vdev %i create failed: ret %d\n", + arvif->vdev_id, ret); goto err; } @@ -2535,7 +2557,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, arvif->def_wep_key_idx); if (ret) { - ath10k_warn("Failed to set default keyid: %d\n", ret); + ath10k_warn("Failed to set vdev %i default keyid: %d\n", + arvif->vdev_id, ret); goto err_vdev_delete; } @@ -2544,21 +2567,23 @@ 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 TX encap: %d\n", ret); + ath10k_warn("Failed to set vdev %i TX encap: %d\n", + arvif->vdev_id, ret); goto err_vdev_delete; } 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 peer for AP: %d\n", ret); + ath10k_warn("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 kickout parameters: %d\n", - ret); + ath10k_warn("Failed to set vdev %i kickout parameters: %d\n", + arvif->vdev_id, ret); goto err_peer_delete; } } @@ -2569,7 +2594,8 @@ 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 RX wake policy: %d\n", ret); + ath10k_warn("Failed to set vdev %i RX wake policy: %d\n", + arvif->vdev_id, ret); goto err_peer_delete; } @@ -2578,7 +2604,8 @@ 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 TX wake thresh: %d\n", ret); + ath10k_warn("Failed to set vdev %i TX wake thresh: %d\n", + arvif->vdev_id, ret); goto err_peer_delete; } @@ -2587,7 +2614,8 @@ 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 PSPOLL count: %d\n", ret); + ath10k_warn("Failed to set vdev %i PSPOLL count: %d\n", + arvif->vdev_id, ret); goto err_peer_delete; } } @@ -2651,17 +2679,19 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, 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: %d\n", ret); + ath10k_warn("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 delete %d (remove interface)\n", + ath10k_dbg(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("WMI vdev delete failed: %d\n", ret); + ath10k_warn("WMI vdev %i delete failed: %d\n", + arvif->vdev_id, ret); if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ar->monitor_present = false; @@ -2750,8 +2780,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, arvif->vdev_id, arvif->beacon_interval); if (ret) - ath10k_warn("Failed to set beacon interval for VDEV: %d\n", - arvif->vdev_id); + ath10k_warn("Failed to set beacon interval for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_BEACON) { @@ -2763,8 +2793,8 @@ 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\n", - arvif->vdev_id); + ath10k_warn("Failed to set beacon mode for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_BEACON_INFO) { @@ -2778,8 +2808,8 @@ 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\n", - arvif->vdev_id); + ath10k_warn("Failed to set dtim period for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_SSID && @@ -2799,7 +2829,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ret = ath10k_peer_create(ar, arvif->vdev_id, info->bssid); if (ret) - ath10k_warn("Failed to add peer %pM for vdev %d when changin bssid: %i\n", + ath10k_warn("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) { @@ -2815,8 +2845,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ret = ath10k_vdev_start(arvif); if (ret) { - ath10k_warn("failed to start vdev: %d\n", - ret); + ath10k_warn("failed to start vdev %i: %d\n", + arvif->vdev_id, ret); goto exit; } @@ -2851,8 +2881,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, cts_prot); if (ret) - ath10k_warn("Failed to set CTS prot for VDEV: %d\n", - arvif->vdev_id); + ath10k_warn("Failed to set CTS prot for vdev %d: %d\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_ERP_SLOT) { @@ -2870,8 +2900,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, 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\n", - arvif->vdev_id); + ath10k_warn("Failed to set erp slot for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_ERP_PREAMBLE) { @@ -2889,8 +2919,8 @@ 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\n", - arvif->vdev_id); + ath10k_warn("Failed to set preamble for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_ASSOC) { @@ -3021,8 +3051,8 @@ 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 group key as default key: %d\n", - ret); + ath10k_warn("failed to set vdev %i group key as default key: %d\n", + arvif->vdev_id, ret); } static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -3082,7 +3112,8 @@ 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("ath10k_install_key failed (%d)\n", ret); + ath10k_warn("key installation failed for vdev %i peer %pM: %d\n", + arvif->vdev_id, peer_addr, ret); goto exit; } @@ -3179,6 +3210,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, int max_num_peers; int ret = 0; + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + memset(arsta, 0, sizeof(*arsta)); + arsta->arvif = arvif; + INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk); + } + /* cancel must be done outside the mutex to avoid deadlock */ if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) @@ -3208,10 +3246,6 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, "mac vdev %d peer create %pM (new sta) num_peers %d\n", arvif->vdev_id, sta->addr, ar->num_peers); - memset(arsta, 0, sizeof(*arsta)); - arsta->arvif = arvif; - INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk); - 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", @@ -3226,8 +3260,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, 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\n", - sta->addr, arvif->vdev_id); + ath10k_warn("Failed to delete peer %pM for vdev %d: %i\n", + sta->addr, arvif->vdev_id, ret); if (vif->type == NL80211_IFTYPE_STATION) ath10k_bss_disassoc(hw, vif); @@ -3243,8 +3277,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, ret = ath10k_station_assoc(ar, arvif, sta); if (ret) - ath10k_warn("Failed to associate station: %pM\n", - sta->addr); + ath10k_warn("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 && (vif->type == NL80211_IFTYPE_AP || @@ -3257,8 +3291,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, ret = ath10k_station_disassoc(ar, arvif, sta); if (ret) - ath10k_warn("Failed to disassociate station: %pM\n", - sta->addr); + ath10k_warn("Failed to disassociate station: %pM vdev %i ret %i\n", + sta->addr, arvif->vdev_id, ret); } exit: mutex_unlock(&ar->conf_mutex); @@ -3539,7 +3573,8 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) }), ATH10K_FLUSH_TIMEOUT_HZ); if (ret <= 0 || skip) - ath10k_warn("tx not flushed\n"); + ath10k_warn("tx not flushed (skip %i ar-state %i): %i\n", + skip, ar->state, ret); skip: mutex_unlock(&ar->conf_mutex); @@ -3905,7 +3940,8 @@ static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask, static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, u8 fixed_rate, - u8 fixed_nss) + u8 fixed_nss, + u8 force_sgi) { struct ath10k *ar = arvif->ar; u32 vdev_param; @@ -3914,12 +3950,16 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, mutex_lock(&ar->conf_mutex); if (arvif->fixed_rate == fixed_rate && - arvif->fixed_nss == fixed_nss) + arvif->fixed_nss == fixed_nss && + arvif->force_sgi == force_sgi) goto exit; if (fixed_rate == WMI_FIXED_RATE_NONE) ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n"); + if (force_sgi) + ath10k_dbg(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); @@ -3945,6 +3985,19 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, arvif->fixed_nss = fixed_nss; + vdev_param = ar->wmi.vdev_param->sgi; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, + force_sgi); + + if (ret) { + ath10k_warn("Could not set sgi param %d: %d\n", + force_sgi, ret); + ret = -EINVAL; + goto exit; + } + + arvif->force_sgi = force_sgi; + exit: mutex_unlock(&ar->conf_mutex); return ret; @@ -3959,6 +4012,11 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, enum ieee80211_band band = ar->hw->conf.chandef.chan->band; u8 fixed_rate = WMI_FIXED_RATE_NONE; u8 fixed_nss = ar->num_rf_chains; + u8 force_sgi; + + force_sgi = mask->control[band].gi; + if (force_sgi == NL80211_TXRATE_FORCE_LGI) + return -EINVAL; if (!ath10k_default_bitrate_mask(ar, band, mask)) { if (!ath10k_get_fixed_rate_nss(mask, band, @@ -3967,7 +4025,13 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, return -EINVAL; } - return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss); + if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) { + ath10k_warn("Could not force SGI usage for default rate settings\n"); + return -EINVAL; + } + + return ath10k_set_fixed_rate_param(arvif, fixed_rate, + fixed_nss, force_sgi); } static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw, @@ -4060,6 +4124,16 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &arsta->update_wk); } +static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + /* + * FIXME: Return 0 for time being. Need to figure out whether FW + * has the API to fetch 64-bit local TSF + */ + + return 0; +} + static const struct ieee80211_ops ath10k_ops = { .tx = ath10k_tx, .start = ath10k_start, @@ -4085,6 +4159,7 @@ static const struct ieee80211_ops ath10k_ops = { .set_bitrate_mask = ath10k_set_bitrate_mask, .channel_switch_beacon = ath10k_channel_switch_beacon, .sta_rc_update = ath10k_sta_rc_update, + .get_tsf = ath10k_get_tsf, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, @@ -4361,7 +4436,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("No VIF found for vdev %d\n", vdev_id); return NULL; } @@ -4442,7 +4517,8 @@ int ath10k_mac_register(struct ath10k *ar) IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_SUPPORTS_STATIC_SMPS | IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_AP_LINK_PS; + IEEE80211_HW_AP_LINK_PS | + IEEE80211_HW_SPECTRUM_MGMT; /* MSDU can have HTT TX fragment pushed in front. The additional 4 * bytes is used for padding/alignment if necessary. */ @@ -4500,7 +4576,7 @@ int ath10k_mac_register(struct ath10k *ar) ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, ath10k_reg_notifier); if (ret) { - ath10k_err("Regulatory initialization failed\n"); + ath10k_err("Regulatory initialization failed: %i\n", ret); goto err_free; } diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 34f09106f42..9d242d801d9 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -58,12 +58,10 @@ static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, u32 *data); -static void ath10k_pci_process_ce(struct ath10k *ar); 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_stop_ce(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); @@ -74,7 +72,6 @@ static void ath10k_pci_free_irq(struct ath10k *ar); static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, struct ath10k_ce_pipe *rx_pipe, struct bmi_xfer *xfer); -static void ath10k_pci_cleanup_ce(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -679,34 +676,12 @@ void ath10k_do_pci_sleep(struct ath10k *ar) } } -/* - * FIXME: Handle OOM properly. - */ -static inline -struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info) -{ - struct ath10k_pci_compl *compl = NULL; - - spin_lock_bh(&pipe_info->pipe_lock); - if (list_empty(&pipe_info->compl_free)) { - ath10k_warn("Completion buffers are full\n"); - goto exit; - } - compl = list_first_entry(&pipe_info->compl_free, - struct ath10k_pci_compl, list); - list_del(&compl->list); -exit: - spin_unlock_bh(&pipe_info->pipe_lock); - return compl; -} - /* Called by lower (CE) layer when a send to Target completes. */ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; - struct ath10k_pci_compl *compl; + struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; void *transfer_context; u32 ce_data; unsigned int nbytes; @@ -715,27 +690,12 @@ static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state) while (ath10k_ce_completed_send_next(ce_state, &transfer_context, &ce_data, &nbytes, &transfer_id) == 0) { - compl = get_free_compl(pipe_info); - if (!compl) - break; - - compl->state = ATH10K_PCI_COMPL_SEND; - compl->ce_state = ce_state; - compl->pipe_info = pipe_info; - compl->skb = transfer_context; - compl->nbytes = nbytes; - compl->transfer_id = transfer_id; - compl->flags = 0; + /* no need to call tx completion for NULL pointers */ + if (transfer_context == NULL) + continue; - /* - * Add the completion to the processing queue. - */ - spin_lock_bh(&ar_pci->compl_lock); - list_add_tail(&compl->list, &ar_pci->compl_process); - spin_unlock_bh(&ar_pci->compl_lock); + cb->tx_completion(ar, transfer_context, transfer_id); } - - ath10k_pci_process_ce(ar); } /* Called by lower (CE) layer when data is received from the Target. */ @@ -744,77 +704,100 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; - struct ath10k_pci_compl *compl; + struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; struct sk_buff *skb; void *transfer_context; u32 ce_data; - unsigned int nbytes; + unsigned int nbytes, max_nbytes; unsigned int transfer_id; unsigned int flags; + int err; while (ath10k_ce_completed_recv_next(ce_state, &transfer_context, &ce_data, &nbytes, &transfer_id, &flags) == 0) { - compl = get_free_compl(pipe_info); - if (!compl) - break; - - compl->state = ATH10K_PCI_COMPL_RECV; - compl->ce_state = ce_state; - compl->pipe_info = pipe_info; - compl->skb = transfer_context; - compl->nbytes = nbytes; - compl->transfer_id = transfer_id; - compl->flags = flags; + err = ath10k_pci_post_rx_pipe(pipe_info, 1); + if (unlikely(err)) { + /* FIXME: retry */ + ath10k_warn("failed to replenish CE rx ring %d: %d\n", + pipe_info->pipe_num, err); + } skb = transfer_context; + max_nbytes = skb->len + skb_tailroom(skb); dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - /* - * Add the completion to the processing queue. - */ - spin_lock_bh(&ar_pci->compl_lock); - list_add_tail(&compl->list, &ar_pci->compl_process); - spin_unlock_bh(&ar_pci->compl_lock); - } + max_nbytes, DMA_FROM_DEVICE); - ath10k_pci_process_ce(ar); + if (unlikely(max_nbytes < nbytes)) { + ath10k_warn("rxed more than expected (nbytes %d, max %d)", + nbytes, max_nbytes); + dev_kfree_skb_any(skb); + continue; + } + + skb_put(skb, nbytes); + cb->rx_completion(ar, skb, pipe_info->pipe_num); + } } -/* Send the first nbytes bytes of the buffer */ -static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, - unsigned int transfer_id, - unsigned int bytes, struct sk_buff *nbuf) +static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, + struct ath10k_hif_sg_item *items, int n_items) { - struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe_id]); - struct ath10k_ce_pipe *ce_hdl = pipe_info->ce_hdl; - unsigned int len; - u32 flags = 0; - int ret; + struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id]; + struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl; + struct ath10k_ce_ring *src_ring = ce_pipe->src_ring; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int sw_index = src_ring->sw_index; + unsigned int write_index = src_ring->write_index; + int err, i; - len = min(bytes, nbuf->len); - bytes -= len; + spin_lock_bh(&ar_pci->ce_lock); - if (len & 3) - ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len); + if (unlikely(CE_RING_DELTA(nentries_mask, + write_index, sw_index - 1) < n_items)) { + err = -ENOBUFS; + goto unlock; + } - ath10k_dbg(ATH10K_DBG_PCI, - "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n", - nbuf->data, (unsigned long long) skb_cb->paddr, - nbuf->len, len); - ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, - "ath10k tx: data: ", - nbuf->data, nbuf->len); - - ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id, - flags); - if (ret) - ath10k_warn("failed to send sk_buff to CE: %p\n", nbuf); + for (i = 0; i < n_items - 1; i++) { + ath10k_dbg(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: ", + items[i].vaddr, items[i].len); - return ret; + err = ath10k_ce_send_nolock(ce_pipe, + items[i].transfer_context, + items[i].paddr, + items[i].len, + items[i].transfer_id, + CE_SEND_FLAG_GATHER); + if (err) + goto unlock; + } + + /* `i` is equal to `n_items -1` after for() */ + + ath10k_dbg(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: ", + items[i].vaddr, items[i].len); + + err = ath10k_ce_send_nolock(ce_pipe, + items[i].transfer_context, + items[i].paddr, + items[i].len, + items[i].transfer_id, + 0); + if (err) + goto unlock; + + err = 0; +unlock: + spin_unlock_bh(&ar_pci->ce_lock); + return err; } static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) @@ -903,52 +886,6 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, sizeof(ar_pci->msg_callbacks_current)); } -static int ath10k_pci_alloc_compl(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - const struct ce_attr *attr; - struct ath10k_pci_pipe *pipe_info; - struct ath10k_pci_compl *compl; - int i, pipe_num, completions; - - spin_lock_init(&ar_pci->compl_lock); - INIT_LIST_HEAD(&ar_pci->compl_process); - - for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { - pipe_info = &ar_pci->pipe_info[pipe_num]; - - spin_lock_init(&pipe_info->pipe_lock); - INIT_LIST_HEAD(&pipe_info->compl_free); - - /* Handle Diagnostic CE specially */ - if (pipe_info->ce_hdl == ar_pci->ce_diag) - continue; - - attr = &host_ce_config_wlan[pipe_num]; - completions = 0; - - if (attr->src_nentries) - completions += attr->src_nentries; - - if (attr->dest_nentries) - completions += attr->dest_nentries; - - for (i = 0; i < completions; i++) { - compl = kmalloc(sizeof(*compl), GFP_KERNEL); - if (!compl) { - ath10k_warn("No memory for completion state\n"); - ath10k_pci_cleanup_ce(ar); - return -ENOMEM; - } - - compl->state = ATH10K_PCI_COMPL_FREE; - list_add_tail(&compl->list, &pipe_info->compl_free); - } - } - - return 0; -} - static int ath10k_pci_setup_ce_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -993,147 +930,6 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) tasklet_kill(&ar_pci->pipe_info[i].intr); } -static void ath10k_pci_stop_ce(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_compl *compl; - struct sk_buff *skb; - - /* Mark pending completions as aborted, so that upper layers free up - * their associated resources */ - spin_lock_bh(&ar_pci->compl_lock); - list_for_each_entry(compl, &ar_pci->compl_process, list) { - skb = compl->skb; - ATH10K_SKB_CB(skb)->is_aborted = true; - } - spin_unlock_bh(&ar_pci->compl_lock); -} - -static void ath10k_pci_cleanup_ce(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_compl *compl, *tmp; - struct ath10k_pci_pipe *pipe_info; - struct sk_buff *netbuf; - int pipe_num; - - /* Free pending completions. */ - spin_lock_bh(&ar_pci->compl_lock); - if (!list_empty(&ar_pci->compl_process)) - ath10k_warn("pending completions still present! possible memory leaks.\n"); - - list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) { - list_del(&compl->list); - netbuf = compl->skb; - dev_kfree_skb_any(netbuf); - kfree(compl); - } - spin_unlock_bh(&ar_pci->compl_lock); - - /* Free unused completions for each pipe. */ - for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { - pipe_info = &ar_pci->pipe_info[pipe_num]; - - spin_lock_bh(&pipe_info->pipe_lock); - list_for_each_entry_safe(compl, tmp, - &pipe_info->compl_free, list) { - list_del(&compl->list); - kfree(compl); - } - spin_unlock_bh(&pipe_info->pipe_lock); - } -} - -static void ath10k_pci_process_ce(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ar->hif.priv; - struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; - struct ath10k_pci_compl *compl; - struct sk_buff *skb; - unsigned int nbytes; - int ret, send_done = 0; - - /* Upper layers aren't ready to handle tx/rx completions in parallel so - * we must serialize all completion processing. */ - - spin_lock_bh(&ar_pci->compl_lock); - if (ar_pci->compl_processing) { - spin_unlock_bh(&ar_pci->compl_lock); - return; - } - ar_pci->compl_processing = true; - spin_unlock_bh(&ar_pci->compl_lock); - - for (;;) { - spin_lock_bh(&ar_pci->compl_lock); - if (list_empty(&ar_pci->compl_process)) { - spin_unlock_bh(&ar_pci->compl_lock); - break; - } - compl = list_first_entry(&ar_pci->compl_process, - struct ath10k_pci_compl, list); - list_del(&compl->list); - spin_unlock_bh(&ar_pci->compl_lock); - - switch (compl->state) { - case ATH10K_PCI_COMPL_SEND: - cb->tx_completion(ar, - compl->skb, - compl->transfer_id); - send_done = 1; - break; - case ATH10K_PCI_COMPL_RECV: - ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1); - if (ret) { - ath10k_warn("failed to post RX buffer for pipe %d: %d\n", - compl->pipe_info->pipe_num, ret); - break; - } - - skb = compl->skb; - nbytes = compl->nbytes; - - ath10k_dbg(ATH10K_DBG_PCI, - "ath10k_pci_ce_recv_data netbuf=%p nbytes=%d\n", - skb, nbytes); - ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, - "ath10k rx: ", skb->data, nbytes); - - if (skb->len + skb_tailroom(skb) >= nbytes) { - skb_trim(skb, 0); - skb_put(skb, nbytes); - cb->rx_completion(ar, skb, - compl->pipe_info->pipe_num); - } else { - ath10k_warn("rxed more than expected (nbytes %d, max %d)", - nbytes, - skb->len + skb_tailroom(skb)); - } - break; - case ATH10K_PCI_COMPL_FREE: - ath10k_warn("free completion cannot be processed\n"); - break; - default: - ath10k_warn("invalid completion state (%d)\n", - compl->state); - break; - } - - compl->state = ATH10K_PCI_COMPL_FREE; - - /* - * Add completion back to the pipe's free list. - */ - spin_lock_bh(&compl->pipe_info->pipe_lock); - list_add_tail(&compl->list, &compl->pipe_info->compl_free); - spin_unlock_bh(&compl->pipe_info->pipe_lock); - } - - spin_lock_bh(&ar_pci->compl_lock); - ar_pci->compl_processing = false; - spin_unlock_bh(&ar_pci->compl_lock); -} - /* 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, @@ -1305,17 +1101,11 @@ static int ath10k_pci_hif_start(struct ath10k *ar) ath10k_pci_free_early_irq(ar); ath10k_pci_kill_tasklet(ar); - ret = ath10k_pci_alloc_compl(ar); - if (ret) { - ath10k_warn("failed to allocate CE completions: %d\n", ret); - goto err_early_irq; - } - ret = ath10k_pci_request_irq(ar); if (ret) { ath10k_warn("failed to post RX buffers for all pipes: %d\n", ret); - goto err_free_compl; + goto err_early_irq; } ret = ath10k_pci_setup_ce_irq(ar); @@ -1339,10 +1129,6 @@ err_stop: ath10k_ce_disable_interrupts(ar); ath10k_pci_free_irq(ar); ath10k_pci_kill_tasklet(ar); - ath10k_pci_stop_ce(ar); - ath10k_pci_process_ce(ar); -err_free_compl: - ath10k_pci_cleanup_ce(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 @@ -1413,18 +1199,10 @@ static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf, &ce_data, &nbytes, &id) == 0) { - /* - * Indicate the completion to higer layer to free - * the buffer - */ - - if (!netbuf) { - ath10k_warn("invalid sk_buff on CE %d - NULL pointer. firmware crashed?\n", - ce_hdl->id); + /* no need to call tx completion for NULL pointers */ + if (!netbuf) continue; - } - ATH10K_SKB_CB(netbuf)->is_aborted = true; ar_pci->msg_callbacks_current.tx_completion(ar, netbuf, id); @@ -1482,7 +1260,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) ath10k_pci_free_irq(ar); ath10k_pci_kill_tasklet(ar); - ath10k_pci_stop_ce(ar); ret = ath10k_pci_request_early_irq(ar); if (ret) @@ -1492,8 +1269,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) * not DMA nor interrupt. We process the leftovers and then free * everything else up. */ - ath10k_pci_process_ce(ar); - ath10k_pci_cleanup_ce(ar); ath10k_pci_buffer_cleanup(ar); /* Make the sure the device won't access any structures on the host by @@ -2269,7 +2044,7 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) #endif static const struct ath10k_hif_ops ath10k_pci_hif_ops = { - .send_head = ath10k_pci_hif_send_head, + .tx_sg = ath10k_pci_hif_tx_sg, .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, .start = ath10k_pci_hif_start, .stop = ath10k_pci_hif_stop, diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index a4f32038c44..b43fdb4f731 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -43,23 +43,6 @@ struct bmi_xfer { u32 resp_len; }; -enum ath10k_pci_compl_state { - ATH10K_PCI_COMPL_FREE = 0, - ATH10K_PCI_COMPL_SEND, - ATH10K_PCI_COMPL_RECV, -}; - -struct ath10k_pci_compl { - struct list_head list; - enum ath10k_pci_compl_state state; - struct ath10k_ce_pipe *ce_state; - struct ath10k_pci_pipe *pipe_info; - struct sk_buff *skb; - unsigned int nbytes; - unsigned int transfer_id; - unsigned int flags; -}; - /* * PCI-specific Target state * @@ -175,9 +158,6 @@ struct ath10k_pci_pipe { /* protects compl_free and num_send_allowed */ spinlock_t pipe_lock; - /* List of free CE completion slots */ - struct list_head compl_free; - struct ath10k_pci *ar_pci; struct tasklet_struct intr; }; @@ -205,14 +185,6 @@ struct ath10k_pci { atomic_t keep_awake_count; bool verified_awake; - /* List of CE completions to be processed */ - struct list_head compl_process; - - /* protects compl_processing and compl_process */ - spinlock_t compl_lock; - - bool compl_processing; - struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX]; struct ath10k_hif_cb msg_callbacks_current; diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index ec6f82521b0..0541dd939ce 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -51,7 +51,8 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct ieee80211_tx_info *info; struct ath10k_skb_cb *skb_cb; struct sk_buff *msdu; - int ret; + + lockdep_assert_held(&htt->tx_lock); ath10k_dbg(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); @@ -65,12 +66,12 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, msdu = htt->pending_tx[tx_done->msdu_id]; skb_cb = ATH10K_SKB_CB(msdu); - ret = ath10k_skb_unmap(dev, msdu); - if (ret) - ath10k_warn("data skb unmap failed (%d)\n", ret); + dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->htt.frag_len) - skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len); + if (skb_cb->htt.txbuf) + dma_pool_free(htt->tx_pool, + skb_cb->htt.txbuf, + skb_cb->htt.txbuf_paddr); ath10k_report_offchan_tx(htt->ar, msdu); @@ -92,13 +93,11 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, /* we do not own the msdu anymore */ exit: - spin_lock_bh(&htt->tx_lock); htt->pending_tx[tx_done->msdu_id] = NULL; ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); __ath10k_htt_tx_dec_pending(htt); if (htt->num_pending_tx == 0) wake_up(&htt->empty_tx_wq); - spin_unlock_bh(&htt->tx_lock); } static const u8 rx_legacy_rate_idx[] = { @@ -258,6 +257,12 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) status->band = ch->band; status->freq = ch->center_freq; + if (info->rate.info0 & HTT_RX_INDICATION_INFO0_END_VALID) { + /* TSF available only in 32-bit */ + status->mactime = info->tsf & 0xffffffff; + status->flag |= RX_FLAG_MACTIME_END; + } + 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 %i\n", info->skb, @@ -378,7 +383,8 @@ 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("unknown peer id %d\n", ev->peer_id); + ath10k_warn("peer-unmap-event: unknown peer id %d\n", + ev->peer_id); goto exit; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 91e501b5499..cb1f7b5bcf4 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1360,7 +1360,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) struct wmi_bcn_info *bcn_info; struct ath10k_vif *arvif; struct sk_buff *bcn; - int vdev_id = 0; + int ret, vdev_id = 0; ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n"); @@ -1435,16 +1435,27 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ath10k_warn("SWBA overrun on vdev %d\n", arvif->vdev_id); - ath10k_skb_unmap(ar->dev, arvif->beacon); + dma_unmap_single(arvif->ar->dev, + ATH10K_SKB_CB(arvif->beacon)->paddr, + arvif->beacon->len, DMA_TO_DEVICE); dev_kfree_skb_any(arvif->beacon); } - ath10k_skb_map(ar->dev, bcn); + ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev, + bcn->data, bcn->len, + DMA_TO_DEVICE); + ret = dma_mapping_error(arvif->ar->dev, + ATH10K_SKB_CB(bcn)->paddr); + if (ret) { + ath10k_warn("failed to map beacon: %d\n", ret); + goto skip; + } arvif->beacon = bcn; arvif->beacon_sent = false; ath10k_wmi_tx_beacon_nowait(arvif); +skip: spin_unlock_bh(&ar->data_lock); } } @@ -3382,7 +3393,6 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, ci->max_power = ch->max_power; ci->reg_power = ch->max_reg_power; ci->antenna_max = ch->max_antenna_gain; - ci->antenna_max = 0; /* mode & flags share storage */ ci->mode = ch->mode; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index ef35da84f63..4b18434ba69 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -751,6 +751,9 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(ah->dev, bf->skbaddr)) + return -ENOSPC; + ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates, ARRAY_SIZE(bf->rates)); diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index b58fe99ef74..8e1c7b0fe76 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -52,7 +52,8 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o ath9k_common-y:= common.o \ - common-init.o + common-init.o \ + common-beacon.o ath9k_htc-y += htc_hst.o \ hif_usb.o \ diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 2dff2765769..a0398fe3eb2 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -39,6 +39,10 @@ static const struct platform_device_id ath9k_platform_id_table[] = { .name = "qca955x_wmac", .driver_data = AR9300_DEVID_QCA955X, }, + { + .name = "qca953x_wmac", + .driver_data = AR9300_DEVID_AR953X, + }, {}, }; @@ -82,6 +86,7 @@ static int ath_ahb_probe(struct platform_device *pdev) int irq; int ret = 0; struct ath_hw *ah; + struct ath_common *common; char hw_name[64]; if (!dev_get_platdata(&pdev->dev)) { @@ -124,9 +129,6 @@ static int ath_ahb_probe(struct platform_device *pdev) sc->mem = mem; sc->irq = irq; - /* Will be cleared in ath9k_start() */ - set_bit(SC_OP_INVALID, &sc->sc_flags); - ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); @@ -144,6 +146,9 @@ static int ath_ahb_probe(struct platform_device *pdev) wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n", hw_name, (unsigned long)mem, irq); + common = ath9k_hw_common(sc->sc_ah); + /* Will be cleared in ath9k_start() */ + set_bit(ATH_OP_INVALID, &common->op_flags); return 0; err_irq: diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 2ce5079007b..6d47783f2e5 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -318,17 +318,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) BUG_ON(aniState == NULL); ah->stats.ast_ani_reset++; - /* only allow a subset of functions in AP mode */ - if (ah->opmode == NL80211_IFTYPE_AP) { - if (IS_CHAN_2GHZ(chan)) { - ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | - ATH9K_ANI_FIRSTEP_LEVEL); - if (AR_SREV_9300_20_OR_LATER(ah)) - ah->ani_function |= ATH9K_ANI_MRC_CCK; - } else - ah->ani_function = 0; - } - ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL, aniState->ofdmNoiseImmunityLevel); cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL, diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index ff415e863ee..3b3e91057a4 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -26,10 +26,6 @@ static const int firstep_table[] = /* level: 0 1 2 3 4 5 6 7 8 */ { -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */ -static const int cycpwrThr1_table[] = -/* level: 0 1 2 3 4 5 6 7 8 */ - { -6, -4, -2, 0, 2, 4, 6, 8 }; /* lvl 0-7, default 3 */ - /* * register values to turn OFDM weak signal detection OFF */ @@ -921,7 +917,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ar5416AniState *aniState = &ah->ani; - s32 value, value2; + s32 value; switch (cmd & ah->ani_function) { case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ @@ -1008,42 +1004,11 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, case ATH9K_ANI_FIRSTEP_LEVEL:{ u32 level = param; - if (level >= ARRAY_SIZE(firstep_table)) { - ath_dbg(common, ANI, - "ATH9K_ANI_FIRSTEP_LEVEL: level out of range (%u > %zu)\n", - level, ARRAY_SIZE(firstep_table)); - return false; - } - - /* - * make register setting relative to default - * from INI file & cap value - */ - value = firstep_table[level] - - firstep_table[ATH9K_ANI_FIRSTEP_LVL] + - aniState->iniDef.firstep; - if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN) - value = ATH9K_SIG_FIRSTEP_SETTING_MIN; - if (value > ATH9K_SIG_FIRSTEP_SETTING_MAX) - value = ATH9K_SIG_FIRSTEP_SETTING_MAX; + value = level * 2; REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, - AR_PHY_FIND_SIG_FIRSTEP, - value); - /* - * we need to set first step low register too - * make register setting relative to default - * from INI file & cap value - */ - value2 = firstep_table[level] - - firstep_table[ATH9K_ANI_FIRSTEP_LVL] + - aniState->iniDef.firstepLow; - if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN) - value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN; - if (value2 > ATH9K_SIG_FIRSTEP_SETTING_MAX) - value2 = ATH9K_SIG_FIRSTEP_SETTING_MAX; - + AR_PHY_FIND_SIG_FIRSTEP, value); REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW, - AR_PHY_FIND_SIG_FIRSTEP_LOW, value2); + AR_PHY_FIND_SIG_FIRSTEP_LOW, value); if (level != aniState->firstepLevel) { ath_dbg(common, ANI, @@ -1060,7 +1025,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, aniState->firstepLevel, level, ATH9K_ANI_FIRSTEP_LVL, - value2, + value, aniState->iniDef.firstepLow); if (level > aniState->firstepLevel) ah->stats.ast_ani_stepup++; @@ -1073,41 +1038,13 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{ u32 level = param; - if (level >= ARRAY_SIZE(cycpwrThr1_table)) { - ath_dbg(common, ANI, - "ATH9K_ANI_SPUR_IMMUNITY_LEVEL: level out of range (%u > %zu)\n", - level, ARRAY_SIZE(cycpwrThr1_table)); - return false; - } - /* - * make register setting relative to default - * from INI file & cap value - */ - value = cycpwrThr1_table[level] - - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + - aniState->iniDef.cycpwrThr1; - if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN) - value = ATH9K_SIG_SPUR_IMM_SETTING_MIN; - if (value > ATH9K_SIG_SPUR_IMM_SETTING_MAX) - value = ATH9K_SIG_SPUR_IMM_SETTING_MAX; + value = (level + 1) * 2; REG_RMW_FIELD(ah, AR_PHY_TIMING5, - AR_PHY_TIMING5_CYCPWR_THR1, - value); + AR_PHY_TIMING5_CYCPWR_THR1, value); - /* - * set AR_PHY_EXT_CCA for extension channel - * make register setting relative to default - * from INI file & cap value - */ - value2 = cycpwrThr1_table[level] - - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + - aniState->iniDef.cycpwrThr1Ext; - if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN) - value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN; - if (value2 > ATH9K_SIG_SPUR_IMM_SETTING_MAX) - value2 = ATH9K_SIG_SPUR_IMM_SETTING_MAX; - REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, - AR_PHY_EXT_TIMING5_CYCPWR_THR1, value2); + if (IS_CHAN_HT40(ah->curchan)) + REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, + AR_PHY_EXT_TIMING5_CYCPWR_THR1, value); if (level != aniState->spurImmunityLevel) { ath_dbg(common, ANI, @@ -1124,7 +1061,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, aniState->spurImmunityLevel, level, ATH9K_ANI_SPUR_IMMUNE_LVL, - value2, + value, aniState->iniDef.cycpwrThr1Ext); if (level > aniState->spurImmunityLevel) ah->stats.ast_ani_spurup++; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index b8daff78b9d..235053ba773 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -23,8 +23,8 @@ #define COMP_HDR_LEN 4 #define COMP_CKSUM_LEN 2 -#define LE16(x) __constant_cpu_to_le16(x) -#define LE32(x) __constant_cpu_to_le32(x) +#define LE16(x) cpu_to_le16(x) +#define LE32(x) cpu_to_le32(x) /* Local defines to distinguish between extension and control CTL's */ #define EXT_ADDITIVE (0x8000) @@ -4792,43 +4792,54 @@ static void ar9003_hw_power_control_override(struct ath_hw *ah, tempslope: if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + u8 txmask = (eep->baseEepHeader.txrxMask & 0xf0) >> 4; + /* * AR955x has tempSlope register for each chain. * Check whether temp_compensation feature is enabled or not. */ if (eep->baseEepHeader.featureEnable & 0x1) { if (frequency < 4000) { - REG_RMW_FIELD(ah, AR_PHY_TPC_19, - AR_PHY_TPC_19_ALPHA_THERM, - eep->base_ext2.tempSlopeLow); - REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, - AR_PHY_TPC_19_ALPHA_THERM, - temp_slope); - REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, - AR_PHY_TPC_19_ALPHA_THERM, - eep->base_ext2.tempSlopeHigh); + if (txmask & BIT(0)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, + eep->base_ext2.tempSlopeLow); + if (txmask & BIT(1)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope); + if (txmask & BIT(2)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, + AR_PHY_TPC_19_ALPHA_THERM, + eep->base_ext2.tempSlopeHigh); } else { - REG_RMW_FIELD(ah, AR_PHY_TPC_19, - AR_PHY_TPC_19_ALPHA_THERM, - temp_slope); - REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, - AR_PHY_TPC_19_ALPHA_THERM, - temp_slope1); - REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, - AR_PHY_TPC_19_ALPHA_THERM, - temp_slope2); + if (txmask & BIT(0)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope); + if (txmask & BIT(1)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope1); + if (txmask & BIT(2)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope2); } } else { /* * If temp compensation is not enabled, * set all registers to 0. */ - REG_RMW_FIELD(ah, AR_PHY_TPC_19, - AR_PHY_TPC_19_ALPHA_THERM, 0); - REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, - AR_PHY_TPC_19_ALPHA_THERM, 0); - REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, - AR_PHY_TPC_19_ALPHA_THERM, 0); + if (txmask & BIT(0)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, 0); + if (txmask & BIT(1)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, + AR_PHY_TPC_19_ALPHA_THERM, 0); + if (txmask & BIT(2)) + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, + AR_PHY_TPC_19_ALPHA_THERM, 0); } } else { REG_RMW_FIELD(ah, AR_PHY_TPC_19, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f995c374a9b..44d74495c4d 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -403,20 +403,10 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, #define ATH_BCBUF 8 #define ATH_DEFAULT_BINTVAL 100 /* TU */ #define ATH_DEFAULT_BMISS_LIMIT 10 -#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) #define TSF_TO_TU(_h,_l) \ ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) -struct ath_beacon_config { - int beacon_interval; - u16 dtim_period; - u16 bmiss_timeout; - u8 dtim_count; - bool enable_beacon; - bool ibss_creator; -}; - struct ath_beacon { enum { OK, /* no change needed */ @@ -426,11 +416,9 @@ struct ath_beacon { u32 beaconq; u32 bmisscnt; - u32 bc_tstamp; struct ieee80211_vif *bslot[ATH_BCBUF]; int slottime; int slotupdate; - struct ath9k_tx_queue_info beacon_qi; struct ath_descdma bdma; struct ath_txq *cabq; struct list_head bbuf; @@ -697,15 +685,6 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ #define MAX_GTT_CNT 5 -enum sc_op_flags { - SC_OP_INVALID, - SC_OP_BEACONS, - SC_OP_ANI_RUN, - SC_OP_PRIM_STA_VIF, - SC_OP_HW_RESET, - SC_OP_SCANNING, -}; - /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) #define PS_WAIT_FOR_CAB BIT(1) @@ -735,7 +714,6 @@ struct ath_softc { struct completion paprd_complete; wait_queue_head_t tx_wait; - unsigned long sc_flags; unsigned long driver_data; u8 gtt_cnt; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 02eb4f10332..471e0f624e8 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -328,7 +328,7 @@ void ath9k_beacon_tasklet(unsigned long data) bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); int slot; - if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) { + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) { ath_dbg(common, RESET, "reset work is pending, skip beaconing now\n"); return; @@ -447,33 +447,6 @@ static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, ath9k_hw_enable_interrupts(ah); } -/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */ -static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu) -{ - u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo; - - tsf_mod = tsf & (BIT(10) - 1); - tsf_hi = tsf >> 32; - tsf_lo = ((u32) tsf) >> 10; - - mod_hi = tsf_hi % div_tu; - mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu; - - return (mod_lo << 10) | tsf_mod; -} - -static u32 ath9k_get_next_tbtt(struct ath_softc *sc, u64 tsf, - unsigned int interval) -{ - struct ath_hw *ah = sc->sc_ah; - unsigned int offset; - - tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time); - offset = ath9k_mod_tsf64_tu(tsf, interval); - - return (u32) tsf + TU_TO_USEC(interval) - offset; -} - /* * For multi-bss ap support beacons are either staggered evenly over N slots or * burst together. For the former arrange for the SWBA to be delivered for each @@ -483,109 +456,18 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc, struct ath_beacon_config *conf) { struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - u32 nexttbtt, intval; - - /* NB: the beacon interval is kept internally in TU's */ - intval = TU_TO_USEC(conf->beacon_interval); - intval /= ATH_BCBUF; - nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah), - conf->beacon_interval); - - if (conf->enable_beacon) - ah->imask |= ATH9K_INT_SWBA; - else - ah->imask &= ~ATH9K_INT_SWBA; - ath_dbg(common, BEACON, - "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n", - (conf->enable_beacon) ? "Enable" : "Disable", - nexttbtt, intval, conf->beacon_interval); - - ath9k_beacon_init(sc, nexttbtt, intval, false); + ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF); + ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, false); } -/* - * This sets up the beacon timers according to the timestamp of the last - * received beacon and the current TSF, configures PCF and DTIM - * handling, programs the sleep registers so the hardware will wakeup in - * time to receive beacons, and configures the beacon miss handling so - * we'll receive a BMISS interrupt when we stop seeing beacons from the AP - * we've associated with. - */ -static void ath9k_beacon_config_sta(struct ath_softc *sc, +static void ath9k_beacon_config_sta(struct ath_hw *ah, struct ath_beacon_config *conf) { - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); struct ath9k_beacon_state bs; - int dtim_intval; - u32 nexttbtt = 0, intval; - u64 tsf; - /* No need to configure beacon if we are not associated */ - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { - ath_dbg(common, BEACON, - "STA is not yet associated..skipping beacon config\n"); + if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM) return; - } - - memset(&bs, 0, sizeof(bs)); - intval = conf->beacon_interval; - - /* - * Setup dtim parameters according to - * last beacon we received (which may be none). - */ - dtim_intval = intval * conf->dtim_period; - - /* - * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim state for the result. - */ - tsf = ath9k_hw_gettsf64(ah); - nexttbtt = ath9k_get_next_tbtt(sc, tsf, intval); - - bs.bs_intval = TU_TO_USEC(intval); - bs.bs_dtimperiod = conf->dtim_period * bs.bs_intval; - bs.bs_nexttbtt = nexttbtt; - bs.bs_nextdtim = nexttbtt; - if (conf->dtim_period > 1) - bs.bs_nextdtim = ath9k_get_next_tbtt(sc, tsf, dtim_intval); - - /* - * Calculate the number of consecutive beacons to miss* before taking - * a BMISS interrupt. The configuration is specified in TU so we only - * need calculate based on the beacon interval. Note that we clamp the - * result to at most 15 beacons. - */ - bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); - if (bs.bs_bmissthreshold > 15) - bs.bs_bmissthreshold = 15; - else if (bs.bs_bmissthreshold <= 0) - bs.bs_bmissthreshold = 1; - - /* - * Calculate sleep duration. The configuration is given in ms. - * We ensure a multiple of the beacon period is used. Also, if the sleep - * duration is greater than the DTIM period then it makes senses - * to make it a multiple of that. - * - * XXX fixed at 100ms - */ - - bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), - intval)); - if (bs.bs_sleepduration > bs.bs_dtimperiod) - bs.bs_sleepduration = bs.bs_dtimperiod; - - /* TSF out of range threshold fixed at 1 second */ - bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - - ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration); - - /* Set the computed STA beacon timers */ ath9k_hw_disable_interrupts(ah); ath9k_hw_set_sta_beacon_timers(ah, &bs); @@ -600,36 +482,19 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc, { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - u32 intval, nexttbtt; ath9k_reset_beacon_status(sc); - intval = TU_TO_USEC(conf->beacon_interval); - - if (conf->ibss_creator) - nexttbtt = intval; - else - nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah), - conf->beacon_interval); - - if (conf->enable_beacon) - ah->imask |= ATH9K_INT_SWBA; - else - ah->imask &= ~ATH9K_INT_SWBA; - - ath_dbg(common, BEACON, - "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n", - (conf->enable_beacon) ? "Enable" : "Disable", - nexttbtt, intval, conf->beacon_interval); + ath9k_cmn_beacon_config_adhoc(ah, conf); - ath9k_beacon_init(sc, nexttbtt, intval, conf->ibss_creator); + ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, conf->ibss_creator); /* * Set the global 'beacon has been configured' flag for the * joiner case in IBSS mode. */ if (!conf->ibss_creator && conf->enable_beacon) - set_bit(SC_OP_BEACONS, &sc->sc_flags); + set_bit(ATH_OP_BEACONS, &common->op_flags); } static bool ath9k_allow_beacon_config(struct ath_softc *sc, @@ -649,7 +514,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { if ((vif->type == NL80211_IFTYPE_STATION) && - test_bit(SC_OP_BEACONS, &sc->sc_flags) && + test_bit(ATH_OP_BEACONS, &common->op_flags) && !avp->primary_sta_vif) { ath_dbg(common, CONFIG, "Beacon already configured for a station interface\n"); @@ -700,6 +565,8 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; bool skip_beacon = false; @@ -712,7 +579,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { ath9k_cache_beacon_config(sc, bss_conf); ath9k_set_beacon(sc); - set_bit(SC_OP_BEACONS, &sc->sc_flags); + set_bit(ATH_OP_BEACONS, &common->op_flags); return; } @@ -751,13 +618,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, } /* - * Do not set the SC_OP_BEACONS flag for IBSS joiner mode + * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode * here, it is done in ath9k_beacon_config_adhoc(). */ if (cur_conf->enable_beacon && !skip_beacon) - set_bit(SC_OP_BEACONS, &sc->sc_flags); + set_bit(ATH_OP_BEACONS, &common->op_flags); else - clear_bit(SC_OP_BEACONS, &sc->sc_flags); + clear_bit(ATH_OP_BEACONS, &common->op_flags); } } @@ -775,7 +642,7 @@ void ath9k_set_beacon(struct ath_softc *sc) ath9k_beacon_config_adhoc(sc, cur_conf); break; case NL80211_IFTYPE_STATION: - ath9k_beacon_config_sta(sc, cur_conf); + ath9k_beacon_config_sta(sc->sc_ah, cur_conf); break; default: ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c new file mode 100644 index 00000000000..775d1d20ce0 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-beacon.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2008-2011 Atheros Communications 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 "common.h" + +#define FUDGE 2 + +/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */ +static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu) +{ + u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo; + + tsf_mod = tsf & (BIT(10) - 1); + tsf_hi = tsf >> 32; + tsf_lo = ((u32) tsf) >> 10; + + mod_hi = tsf_hi % div_tu; + mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu; + + return (mod_lo << 10) | tsf_mod; +} + +static u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf, + unsigned int interval) +{ + unsigned int offset; + + tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time); + offset = ath9k_mod_tsf64_tu(tsf, interval); + + return (u32) tsf + TU_TO_USEC(interval) - offset; +} + +/* + * This sets up the beacon timers according to the timestamp of the last + * received beacon and the current TSF, configures PCF and DTIM + * handling, programs the sleep registers so the hardware will wakeup in + * time to receive beacons, and configures the beacon miss handling so + * we'll receive a BMISS interrupt when we stop seeing beacons from the AP + * we've associated with. + */ +int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, + struct ath_beacon_config *conf, + struct ath9k_beacon_state *bs) +{ + struct ath_common *common = ath9k_hw_common(ah); + int dtim_intval; + u64 tsf; + + /* No need to configure beacon if we are not associated */ + if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { + ath_dbg(common, BEACON, + "STA is not yet associated..skipping beacon config\n"); + return -EPERM; + } + + memset(bs, 0, sizeof(*bs)); + conf->intval = conf->beacon_interval; + + /* + * Setup dtim parameters according to + * last beacon we received (which may be none). + */ + dtim_intval = conf->intval * conf->dtim_period; + + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim state for the result. + */ + tsf = ath9k_hw_gettsf64(ah); + conf->nexttbtt = ath9k_get_next_tbtt(ah, tsf, conf->intval); + + bs->bs_intval = TU_TO_USEC(conf->intval); + bs->bs_dtimperiod = conf->dtim_period * bs->bs_intval; + bs->bs_nexttbtt = conf->nexttbtt; + bs->bs_nextdtim = conf->nexttbtt; + if (conf->dtim_period > 1) + bs->bs_nextdtim = ath9k_get_next_tbtt(ah, tsf, dtim_intval); + + /* + * Calculate the number of consecutive beacons to miss* before taking + * a BMISS interrupt. The configuration is specified in TU so we only + * need calculate based on the beacon interval. Note that we clamp the + * result to at most 15 beacons. + */ + bs->bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, conf->intval); + if (bs->bs_bmissthreshold > 15) + bs->bs_bmissthreshold = 15; + else if (bs->bs_bmissthreshold <= 0) + bs->bs_bmissthreshold = 1; + + /* + * Calculate sleep duration. The configuration is given in ms. + * We ensure a multiple of the beacon period is used. Also, if the sleep + * duration is greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + + bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), + conf->intval)); + if (bs->bs_sleepduration > bs->bs_dtimperiod) + bs->bs_sleepduration = bs->bs_dtimperiod; + + /* TSF out of range threshold fixed at 1 second */ + bs->bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; + + ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n", + bs->bs_bmissthreshold, bs->bs_sleepduration); + return 0; +} +EXPORT_SYMBOL(ath9k_cmn_beacon_config_sta); + +void ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah, + struct ath_beacon_config *conf) +{ + struct ath_common *common = ath9k_hw_common(ah); + + conf->intval = TU_TO_USEC(conf->beacon_interval); + + if (conf->ibss_creator) + conf->nexttbtt = conf->intval; + else + conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah), + conf->beacon_interval); + + if (conf->enable_beacon) + ah->imask |= ATH9K_INT_SWBA; + else + ah->imask &= ~ATH9K_INT_SWBA; + + ath_dbg(common, BEACON, + "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n", + (conf->enable_beacon) ? "Enable" : "Disable", + conf->nexttbtt, conf->intval, conf->beacon_interval); +} +EXPORT_SYMBOL(ath9k_cmn_beacon_config_adhoc); + +/* + * For multi-bss ap support beacons are either staggered evenly over N slots or + * burst together. For the former arrange for the SWBA to be delivered for each + * slot. Slots that are not occupied will generate nothing. + */ +void ath9k_cmn_beacon_config_ap(struct ath_hw *ah, + struct ath_beacon_config *conf, + unsigned int bc_buf) +{ + struct ath_common *common = ath9k_hw_common(ah); + + /* NB: the beacon interval is kept internally in TU's */ + conf->intval = TU_TO_USEC(conf->beacon_interval); + conf->intval /= bc_buf; + conf->nexttbtt = ath9k_get_next_tbtt(ah, ath9k_hw_gettsf64(ah), + conf->beacon_interval); + + if (conf->enable_beacon) + ah->imask |= ATH9K_INT_SWBA; + else + ah->imask &= ~ATH9K_INT_SWBA; + + ath_dbg(common, BEACON, + "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n", + (conf->enable_beacon) ? "Enable" : "Disable", + conf->nexttbtt, conf->intval, conf->beacon_interval); +} +EXPORT_SYMBOL(ath9k_cmn_beacon_config_ap); diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.h b/drivers/net/wireless/ath/ath9k/common-beacon.h new file mode 100644 index 00000000000..3665d27f0dc --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-beacon.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2009-2011 Atheros Communications 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. + */ + +struct ath_beacon_config; + +int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, + struct ath_beacon_config *conf, + struct ath9k_beacon_state *bs); +void ath9k_cmn_beacon_config_adhoc(struct ath_hw *ah, + struct ath_beacon_config *conf); +void ath9k_cmn_beacon_config_ap(struct ath_hw *ah, + struct ath_beacon_config *conf, + unsigned int bc_buf); diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 4c449e35bd6..ca38116838f 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -22,6 +22,7 @@ #include "hw-ops.h" #include "common-init.h" +#include "common-beacon.h" /* Common header for Atheros 802.11n base driver cores */ @@ -44,6 +45,19 @@ #define ATH_EP_RND(x, mul) \ (((x) + ((mul)/2)) / (mul)) +#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) + +struct ath_beacon_config { + int beacon_interval; + u16 dtim_period; + u16 bmiss_timeout; + u8 dtim_count; + bool enable_beacon; + bool ibss_creator; + u32 nexttbtt; + u32 intval; +}; + bool ath9k_cmn_rx_accept(struct ath_common *common, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *rxs, diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index f8924efdad5..780ff1bee6f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -139,43 +139,41 @@ static ssize_t read_file_ani(struct file *file, char __user *user_buf, const unsigned int size = 1024; ssize_t retval = 0; char *buf; + int i; + struct { + const char *name; + unsigned int val; + } ani_info[] = { + { "ANI RESET", ah->stats.ast_ani_reset }, + { "OFDM LEVEL", ah->ani.ofdmNoiseImmunityLevel }, + { "CCK LEVEL", ah->ani.cckNoiseImmunityLevel }, + { "SPUR UP", ah->stats.ast_ani_spurup }, + { "SPUR DOWN", ah->stats.ast_ani_spurup }, + { "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon }, + { "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff }, + { "MRC-CCK ON", ah->stats.ast_ani_ccklow }, + { "MRC-CCK OFF", ah->stats.ast_ani_cckhigh }, + { "FIR-STEP UP", ah->stats.ast_ani_stepup }, + { "FIR-STEP DOWN", ah->stats.ast_ani_stepdown }, + { "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero }, + { "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs }, + { "CCK ERRORS", ah->stats.ast_ani_cckerrs }, + }; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - if (common->disable_ani) { - len += scnprintf(buf + len, size - len, "%s: %s\n", - "ANI", "DISABLED"); + len += scnprintf(buf + len, size - len, "%15s: %s\n", "ANI", + common->disable_ani ? "DISABLED" : "ENABLED"); + + if (common->disable_ani) goto exit; - } - len += scnprintf(buf + len, size - len, "%15s: %s\n", - "ANI", "ENABLED"); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "ANI RESET", ah->stats.ast_ani_reset); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "SPUR UP", ah->stats.ast_ani_spurup); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "SPUR DOWN", ah->stats.ast_ani_spurup); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "MRC-CCK ON", ah->stats.ast_ani_ccklow); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "MRC-CCK OFF", ah->stats.ast_ani_cckhigh); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "FIR-STEP UP", ah->stats.ast_ani_stepup); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "FIR-STEP DOWN", ah->stats.ast_ani_stepdown); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs); - len += scnprintf(buf + len, size - len, "%15s: %u\n", - "CCK ERRORS", ah->stats.ast_ani_cckerrs); + for (i = 0; i < ARRAY_SIZE(ani_info); i++) + len += scnprintf(buf + len, size - len, "%15s: %u\n", + ani_info[i].name, ani_info[i].val); + exit: if (len > size) len = size; @@ -210,7 +208,7 @@ static ssize_t write_file_ani(struct file *file, common->disable_ani = !ani; if (common->disable_ani) { - clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); + clear_bit(ATH_OP_ANI_RUN, &common->op_flags); ath_stop_ani(sc); } else { ath_check_ani(sc); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 3baf9ceae60..dab1f0cab99 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -39,7 +39,6 @@ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ #define ATH_DEFAULT_BMISS_LIMIT 10 -#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) #define TSF_TO_TU(_h, _l) \ ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) @@ -406,12 +405,18 @@ static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, #define DEFAULT_SWBA_RESPONSE 40 /* in TUs */ #define MIN_SWBA_RESPONSE 10 /* in TUs */ -struct htc_beacon_config { +struct htc_beacon { + enum { + OK, /* no change needed */ + UPDATE, /* update pending */ + COMMIT /* beacon sent, commit change */ + } updateslot; /* slot time update fsm */ + struct ieee80211_vif *bslot[ATH9K_HTC_MAX_BCN_VIF]; - u16 beacon_interval; - u16 dtim_period; - u16 bmiss_timeout; - u32 bmiss_cnt; + u32 bmisscnt; + u32 beaconq; + int slottime; + int slotupdate; }; struct ath_btcoex { @@ -439,12 +444,8 @@ static inline void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ -#define OP_INVALID BIT(0) -#define OP_SCANNING BIT(1) -#define OP_ENABLE_BEACON BIT(2) #define OP_BT_PRIORITY_DETECTED BIT(3) #define OP_BT_SCAN BIT(4) -#define OP_ANI_RUNNING BIT(5) #define OP_TSF_RESET BIT(6) struct ath9k_htc_priv { @@ -489,7 +490,8 @@ struct ath9k_htc_priv { struct ath9k_hw_cal_data caldata; spinlock_t beacon_lock; - struct htc_beacon_config cur_beacon_conf; + struct ath_beacon_config cur_beacon_conf; + struct htc_beacon beacon; struct ath9k_htc_rx rx; struct ath9k_htc_tx tx; @@ -514,7 +516,6 @@ struct ath9k_htc_priv { struct work_struct led_work; #endif - int beaconq; int cabq; int hwq_map[IEEE80211_NUM_ACS]; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index a00ddb9e737..e8b6ec3c1db 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -26,7 +26,7 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); memset(&qi_be, 0, sizeof(struct ath9k_tx_queue_info)); - ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); + ath9k_hw_get_txq_props(ah, priv->beacon.beaconq, &qi); if (priv->ah->opmode == NL80211_IFTYPE_AP || priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) { @@ -54,212 +54,78 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) } - if (!ath9k_hw_set_txq_props(ah, priv->beaconq, &qi)) { + if (!ath9k_hw_set_txq_props(ah, priv->beacon.beaconq, &qi)) { ath_err(ath9k_hw_common(ah), - "Unable to update beacon queue %u!\n", priv->beaconq); + "Unable to update beacon queue %u!\n", priv->beacon.beaconq); } else { - ath9k_hw_resettxqueue(ah, priv->beaconq); + ath9k_hw_resettxqueue(ah, priv->beacon.beaconq); } } - -static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, - struct htc_beacon_config *bss_conf) +/* + * Both nexttbtt and intval have to be in usecs. + */ +static void ath9k_htc_beacon_init(struct ath9k_htc_priv *priv, + struct ath_beacon_config *conf, + bool reset_tsf) { - struct ath_common *common = ath9k_hw_common(priv->ah); - struct ath9k_beacon_state bs; - enum ath9k_int imask = 0; - int dtimperiod, dtimcount; - int bmiss_timeout; - u32 nexttbtt = 0, intval, tsftu; - __be32 htc_imask = 0; - u64 tsf; - int num_beacons, offset, dtim_dec_count; + struct ath_hw *ah = priv->ah; int ret __attribute__ ((unused)); + __be32 htc_imask = 0; u8 cmd_rsp; - memset(&bs, 0, sizeof(bs)); - - intval = bss_conf->beacon_interval; - bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval); - - /* - * Setup dtim parameters according to - * last beacon we received (which may be none). - */ - dtimperiod = bss_conf->dtim_period; - if (dtimperiod <= 0) /* NB: 0 if not known */ - dtimperiod = 1; - dtimcount = 1; - if (dtimcount >= dtimperiod) /* NB: sanity check */ - dtimcount = 0; - - /* - * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim state for the result. - */ - tsf = ath9k_hw_gettsf64(priv->ah); - tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - - num_beacons = tsftu / intval + 1; - offset = tsftu % intval; - nexttbtt = tsftu - offset; - if (offset) - nexttbtt += intval; - - /* DTIM Beacon every dtimperiod Beacon */ - dtim_dec_count = num_beacons % dtimperiod; - dtimcount -= dtim_dec_count; - if (dtimcount < 0) - dtimcount += dtimperiod; - - bs.bs_intval = TU_TO_USEC(intval); - bs.bs_nexttbtt = TU_TO_USEC(nexttbtt); - bs.bs_dtimperiod = dtimperiod * bs.bs_intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount * bs.bs_intval; - - /* - * Calculate the number of consecutive beacons to miss* before taking - * a BMISS interrupt. The configuration is specified in TU so we only - * need calculate based on the beacon interval. Note that we clamp the - * result to at most 15 beacons. - */ - bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval); - if (bs.bs_bmissthreshold > 15) - bs.bs_bmissthreshold = 15; - else if (bs.bs_bmissthreshold <= 0) - bs.bs_bmissthreshold = 1; - - /* - * Calculate sleep duration. The configuration is given in ms. - * We ensure a multiple of the beacon period is used. Also, if the sleep - * duration is greater than the DTIM period then it makes senses - * to make it a multiple of that. - * - * XXX fixed at 100ms - */ - - bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), - intval)); - if (bs.bs_sleepduration > bs.bs_dtimperiod) - bs.bs_sleepduration = bs.bs_dtimperiod; - - /* TSF out of range threshold fixed at 1 second */ - bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - - ath_dbg(common, CONFIG, "intval: %u tsf: %llu tsftu: %u\n", - intval, tsf, tsftu); - ath_dbg(common, CONFIG, "bmiss: %u sleep: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration); - - /* Set the computed STA beacon timers */ + if (conf->intval >= TU_TO_USEC(DEFAULT_SWBA_RESPONSE)) + ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE; + else + ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; WMI_CMD(WMI_DISABLE_INTR_CMDID); - ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); - imask |= ATH9K_INT_BMISS; - htc_imask = cpu_to_be32(imask); + if (reset_tsf) + ath9k_hw_reset_tsf(ah); + ath9k_htc_beaconq_config(priv); + ath9k_hw_beaconinit(ah, conf->nexttbtt, conf->intval); + priv->beacon.bmisscnt = 0; + htc_imask = cpu_to_be32(ah->imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); } -static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, - struct htc_beacon_config *bss_conf) +static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, + struct ath_beacon_config *bss_conf) { - struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_beacon_state bs; enum ath9k_int imask = 0; - u32 nexttbtt, intval, tsftu; __be32 htc_imask = 0; int ret __attribute__ ((unused)); u8 cmd_rsp; - u64 tsf; - - intval = bss_conf->beacon_interval; - intval /= ATH9K_HTC_MAX_BCN_VIF; - nexttbtt = intval; - /* - * To reduce beacon misses under heavy TX load, - * set the beacon response time to a larger value. - */ - if (intval > DEFAULT_SWBA_RESPONSE) - priv->ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE; - else - priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; - - if (test_bit(OP_TSF_RESET, &priv->op_flags)) { - ath9k_hw_reset_tsf(priv->ah); - clear_bit(OP_TSF_RESET, &priv->op_flags); - } else { - /* - * Pull nexttbtt forward to reflect the current TSF. - */ - tsf = ath9k_hw_gettsf64(priv->ah); - tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; - do { - nexttbtt += intval; - } while (nexttbtt < tsftu); - } - - if (test_bit(OP_ENABLE_BEACON, &priv->op_flags)) - imask |= ATH9K_INT_SWBA; - - ath_dbg(common, CONFIG, - "AP Beacon config, intval: %d, nexttbtt: %u, resp_time: %d imask: 0x%x\n", - bss_conf->beacon_interval, nexttbtt, - priv->ah->config.sw_beacon_response_time, imask); - - ath9k_htc_beaconq_config(priv); + if (ath9k_cmn_beacon_config_sta(priv->ah, bss_conf, &bs) == -EPERM) + return; WMI_CMD(WMI_DISABLE_INTR_CMDID); - ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval)); - priv->cur_beacon_conf.bmiss_cnt = 0; + ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); + imask |= ATH9K_INT_BMISS; htc_imask = cpu_to_be32(imask); WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); } -static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, - struct htc_beacon_config *bss_conf) +static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, + struct ath_beacon_config *conf) { - struct ath_common *common = ath9k_hw_common(priv->ah); - enum ath9k_int imask = 0; - u32 nexttbtt, intval, tsftu; - __be32 htc_imask = 0; - int ret __attribute__ ((unused)); - u8 cmd_rsp; - u64 tsf; - - intval = bss_conf->beacon_interval; - nexttbtt = intval; - - /* - * Pull nexttbtt forward to reflect the current TSF. - */ - tsf = ath9k_hw_gettsf64(priv->ah); - tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; - do { - nexttbtt += intval; - } while (nexttbtt < tsftu); - - /* - * Only one IBSS interfce is allowed. - */ - if (intval > DEFAULT_SWBA_RESPONSE) - priv->ah->config.sw_beacon_response_time = DEFAULT_SWBA_RESPONSE; - else - priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; + struct ath_hw *ah = priv->ah; + ah->imask = 0; - if (test_bit(OP_ENABLE_BEACON, &priv->op_flags)) - imask |= ATH9K_INT_SWBA; + ath9k_cmn_beacon_config_ap(ah, conf, ATH9K_HTC_MAX_BCN_VIF); + ath9k_htc_beacon_init(priv, conf, false); +} - ath_dbg(common, CONFIG, - "IBSS Beacon config, intval: %d, nexttbtt: %u, resp_time: %d, imask: 0x%x\n", - bss_conf->beacon_interval, nexttbtt, - priv->ah->config.sw_beacon_response_time, imask); +static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, + struct ath_beacon_config *conf) +{ + struct ath_hw *ah = priv->ah; + ah->imask = 0; - WMI_CMD(WMI_DISABLE_INTR_CMDID); - ath9k_hw_beaconinit(priv->ah, TU_TO_USEC(nexttbtt), TU_TO_USEC(intval)); - priv->cur_beacon_conf.bmiss_cnt = 0; - htc_imask = cpu_to_be32(imask); - WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); + ath9k_cmn_beacon_config_adhoc(ah, conf); + ath9k_htc_beacon_init(priv, conf, conf->ibss_creator); } void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, @@ -279,7 +145,7 @@ static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv, spin_lock_bh(&priv->beacon_lock); - vif = priv->cur_beacon_conf.bslot[slot]; + vif = priv->beacon.bslot[slot]; skb = ieee80211_get_buffered_bc(priv->hw, vif); @@ -340,10 +206,10 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv, spin_lock_bh(&priv->beacon_lock); - vif = priv->cur_beacon_conf.bslot[slot]; + vif = priv->beacon.bslot[slot]; avp = (struct ath9k_htc_vif *)vif->drv_priv; - if (unlikely(test_bit(OP_SCANNING, &priv->op_flags))) { + if (unlikely(test_bit(ATH_OP_SCANNING, &common->op_flags))) { spin_unlock_bh(&priv->beacon_lock); return; } @@ -423,8 +289,8 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, int slot; if (swba->beacon_pending != 0) { - priv->cur_beacon_conf.bmiss_cnt++; - if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) { + priv->beacon.bmisscnt++; + if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) { ath_dbg(common, BSTUCK, "Beacon stuck, HW reset\n"); ieee80211_queue_work(priv->hw, &priv->fatal_work); @@ -432,16 +298,16 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, return; } - if (priv->cur_beacon_conf.bmiss_cnt) { + if (priv->beacon.bmisscnt) { ath_dbg(common, BSTUCK, "Resuming beacon xmit after %u misses\n", - priv->cur_beacon_conf.bmiss_cnt); - priv->cur_beacon_conf.bmiss_cnt = 0; + priv->beacon.bmisscnt); + priv->beacon.bmisscnt = 0; } slot = ath9k_htc_choose_bslot(priv, swba); spin_lock_bh(&priv->beacon_lock); - if (priv->cur_beacon_conf.bslot[slot] == NULL) { + if (priv->beacon.bslot[slot] == NULL) { spin_unlock_bh(&priv->beacon_lock); return; } @@ -460,13 +326,13 @@ void ath9k_htc_assign_bslot(struct ath9k_htc_priv *priv, spin_lock_bh(&priv->beacon_lock); for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) { - if (priv->cur_beacon_conf.bslot[i] == NULL) { + if (priv->beacon.bslot[i] == NULL) { avp->bslot = i; break; } } - priv->cur_beacon_conf.bslot[avp->bslot] = vif; + priv->beacon.bslot[avp->bslot] = vif; spin_unlock_bh(&priv->beacon_lock); ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", @@ -480,7 +346,7 @@ void ath9k_htc_remove_bslot(struct ath9k_htc_priv *priv, struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; spin_lock_bh(&priv->beacon_lock); - priv->cur_beacon_conf.bslot[avp->bslot] = NULL; + priv->beacon.bslot[avp->bslot] = NULL; spin_unlock_bh(&priv->beacon_lock); ath_dbg(common, CONFIG, "Removed interface at beacon slot: %d\n", @@ -496,7 +362,7 @@ void ath9k_htc_set_tsfadjust(struct ath9k_htc_priv *priv, { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *)vif->drv_priv; - struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; u64 tsfadjust; if (avp->bslot == 0) @@ -528,7 +394,7 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); - struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; bool beacon_configured; @@ -583,7 +449,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); - struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; @@ -619,7 +485,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); - struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &priv->cur_beacon_conf; switch (priv->ah->opmode) { case NL80211_IFTYPE_STATION: diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index b22fb64403d..8a3bd5fe3a5 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -405,8 +405,8 @@ static int ath9k_init_queues(struct ath9k_htc_priv *priv) for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++) priv->hwq_map[i] = -1; - priv->beaconq = ath9k_hw_beaconq_setup(priv->ah); - if (priv->beaconq == -1) { + priv->beacon.beaconq = ath9k_hw_beaconq_setup(priv->ah); + if (priv->beacon.beaconq == -1) { ath_err(common, "Unable to setup BEACON xmit queue\n"); goto err; } @@ -459,8 +459,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, struct ath_common *common; int i, ret = 0, csz = 0; - set_bit(OP_INVALID, &priv->op_flags); - ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); if (!ah) return -ENOMEM; @@ -485,6 +483,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, common->priv = priv; common->debug_mask = ath9k_debug; common->btcoex_enabled = ath9k_htc_btcoex_enable == 1; + set_bit(ATH_OP_INVALID, &common->op_flags); spin_lock_init(&priv->beacon_lock); spin_lock_init(&priv->tx.tx_lock); @@ -520,7 +519,8 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, goto err_queues; for (i = 0; i < ATH9K_HTC_MAX_BCN_VIF; i++) - priv->cur_beacon_conf.bslot[i] = NULL; + priv->beacon.bslot[i] = NULL; + priv->beacon.slottime = ATH9K_SLOT_TIME_9; ath9k_cmn_init_channels_rates(common); ath9k_cmn_init_crypto(ah); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 90dad4172b0..f46cd0250e4 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -250,7 +250,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, u8 cmd_rsp; int ret; - if (test_bit(OP_INVALID, &priv->op_flags)) + if (test_bit(ATH_OP_INVALID, &common->op_flags)) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); @@ -304,7 +304,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, htc_start(priv->htc); - if (!test_bit(OP_SCANNING, &priv->op_flags) && + if (!test_bit(ATH_OP_SCANNING, &common->op_flags) && !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) ath9k_htc_vif_reconfig(priv); @@ -748,7 +748,7 @@ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) common->ani.shortcal_timer = timestamp; common->ani.checkani_timer = timestamp; - set_bit(OP_ANI_RUNNING, &priv->op_flags); + set_bit(ATH_OP_ANI_RUN, &common->op_flags); ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); @@ -756,8 +756,9 @@ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv) { + struct ath_common *common = ath9k_hw_common(priv->ah); cancel_delayed_work_sync(&priv->ani_work); - clear_bit(OP_ANI_RUNNING, &priv->op_flags); + clear_bit(ATH_OP_ANI_RUN, &common->op_flags); } void ath9k_htc_ani_work(struct work_struct *work) @@ -942,7 +943,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ath_dbg(common, CONFIG, "Failed to update capability in target\n"); - clear_bit(OP_INVALID, &priv->op_flags); + clear_bit(ATH_OP_INVALID, &common->op_flags); htc_start(priv->htc); spin_lock_bh(&priv->tx.tx_lock); @@ -971,7 +972,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); - if (test_bit(OP_INVALID, &priv->op_flags)) { + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; @@ -1013,7 +1014,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) ath9k_htc_ps_restore(priv); ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); - set_bit(OP_INVALID, &priv->op_flags); + set_bit(ATH_OP_INVALID, &common->op_flags); ath_dbg(common, CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); @@ -1087,7 +1088,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && - !test_bit(OP_ANI_RUNNING, &priv->op_flags)) { + !test_bit(ATH_OP_ANI_RUN, &common->op_flags)) { ath9k_hw_set_tsfadjust(priv->ah, true); ath9k_htc_start_ani(priv); } @@ -1245,13 +1246,14 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, u64 multicast) { struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); u32 rfilt; mutex_lock(&priv->mutex); changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; - if (test_bit(OP_INVALID, &priv->op_flags)) { + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(ath9k_hw_common(priv->ah), ANY, "Unable to configure filter on invalid state\n"); mutex_unlock(&priv->mutex); @@ -1476,6 +1478,7 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) common->curaid = bss_conf->aid; common->last_rssi = ATH_RSSI_DUMMY_MARKER; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); } } @@ -1497,6 +1500,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); + int slottime; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); @@ -1508,6 +1512,9 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, bss_conf->assoc ? priv->num_sta_assoc_vif++ : priv->num_sta_assoc_vif--; + if (!bss_conf->assoc) + clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + if (priv->ah->opmode == NL80211_IFTYPE_STATION) { ath9k_htc_choose_set_bssid(priv); if (bss_conf->assoc && (priv->num_sta_assoc_vif == 1)) @@ -1529,7 +1536,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n", bss_conf->bssid); ath9k_htc_set_tsfadjust(priv, vif); - set_bit(OP_ENABLE_BEACON, &priv->op_flags); + priv->cur_beacon_conf.enable_beacon = 1; ath9k_htc_beacon_config(priv, vif); } @@ -1543,7 +1550,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); - clear_bit(OP_ENABLE_BEACON, &priv->op_flags); + priv->cur_beacon_conf.enable_beacon = 0; ath9k_htc_beacon_config(priv, vif); } } @@ -1569,11 +1576,21 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) - ah->slottime = 9; + slottime = 9; else - ah->slottime = 20; - - ath9k_hw_init_global_settings(ah); + slottime = 20; + if (vif->type == NL80211_IFTYPE_AP) { + /* + * Defer update, so that connected stations can adjust + * their settings at the same time. + * See beacon.c for more details + */ + priv->beacon.slottime = slottime; + priv->beacon.updateslot = UPDATE; + } else { + ah->slottime = slottime; + ath9k_hw_init_global_settings(ah); + } } if (changed & BSS_CHANGED_HT) @@ -1670,10 +1687,11 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); mutex_lock(&priv->mutex); spin_lock_bh(&priv->beacon_lock); - set_bit(OP_SCANNING, &priv->op_flags); + set_bit(ATH_OP_SCANNING, &common->op_flags); spin_unlock_bh(&priv->beacon_lock); cancel_work_sync(&priv->ps_work); ath9k_htc_stop_ani(priv); @@ -1683,10 +1701,11 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) { struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); mutex_lock(&priv->mutex); spin_lock_bh(&priv->beacon_lock); - clear_bit(OP_SCANNING, &priv->op_flags); + clear_bit(ATH_OP_SCANNING, &common->op_flags); spin_unlock_bh(&priv->beacon_lock); ath9k_htc_ps_wakeup(priv); ath9k_htc_vif_reconfig(priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 47b2bfcd822..e8149e3dbdd 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -924,9 +924,10 @@ static void ath9k_htc_opmode_init(struct ath9k_htc_priv *priv) void ath9k_host_rx_init(struct ath9k_htc_priv *priv) { + struct ath_common *common = ath9k_hw_common(priv->ah); ath9k_hw_rxena(priv->ah); ath9k_htc_opmode_init(priv); - ath9k_hw_startpcureceive(priv->ah, test_bit(OP_SCANNING, &priv->op_flags)); + ath9k_hw_startpcureceive(priv->ah, test_bit(ATH_OP_SCANNING, &common->op_flags)); } static inline void convert_htc_flag(struct ath_rx_status *rx_stats, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2509c2ff082..0992f7c70e1 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -882,7 +882,7 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, AR_IMR_RXORN | AR_IMR_BCNMISC; - if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; if (AR_SREV_9300_20_OR_LATER(ah)) { @@ -3048,6 +3048,7 @@ static struct { { AR_SREV_VERSION_9462, "9462" }, { AR_SREV_VERSION_9550, "9550" }, { AR_SREV_VERSION_9565, "9565" }, + { AR_SREV_VERSION_9531, "9531" }, }; /* For devices with external radios */ diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 30dcef5aba1..72a715fe8f2 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -115,13 +115,14 @@ void ath_hw_pll_work(struct work_struct *work) u32 pll_sqsum; struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); /* * ensure that the PLL WAR is executed only * after the STA is associated (or) if the * beaconing had started in interfaces that * uses beacons. */ - if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) + if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) return; if (sc->tx99_state) @@ -414,7 +415,7 @@ void ath_start_ani(struct ath_softc *sc) unsigned long timestamp = jiffies_to_msecs(jiffies); if (common->disable_ani || - !test_bit(SC_OP_ANI_RUN, &sc->sc_flags) || + !test_bit(ATH_OP_ANI_RUN, &common->op_flags) || (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) return; @@ -438,6 +439,7 @@ void ath_stop_ani(struct ath_softc *sc) void ath_check_ani(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; /* @@ -453,23 +455,23 @@ void ath_check_ani(struct ath_softc *sc) * Disable ANI only when there are no * associated stations. */ - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) + if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) goto stop_ani; } } else if (ah->opmode == NL80211_IFTYPE_STATION) { - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) + if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) goto stop_ani; } - if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags)) { - set_bit(SC_OP_ANI_RUN, &sc->sc_flags); + if (!test_bit(ATH_OP_ANI_RUN, &common->op_flags)) { + set_bit(ATH_OP_ANI_RUN, &common->op_flags); ath_start_ani(sc); } return; stop_ani: - clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); + clear_bit(ATH_OP_ANI_RUN, &common->op_flags); ath_stop_ani(sc); } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 5f727588ca2..51ce36f108f 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -827,7 +827,7 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) return; } - if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; async_mask = AR_INTR_MAC_IRQ; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 42a18037004..d69853b848c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -229,16 +229,16 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); - clear_bit(SC_OP_HW_RESET, &sc->sc_flags); + clear_bit(ATH_OP_HW_RESET, &common->op_flags); ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { - if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) + if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) goto work; if (ah->opmode == NL80211_IFTYPE_STATION && - test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { + test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { spin_lock_irqsave(&sc->sc_pm_lock, flags); sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; spin_unlock_irqrestore(&sc->sc_pm_lock, flags); @@ -336,7 +336,7 @@ static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chand int old_pos = -1; int r; - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) + if (test_bit(ATH_OP_INVALID, &common->op_flags)) return -EIO; offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); @@ -402,7 +402,7 @@ static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chand chan->center_freq); } else { /* perform spectral scan if requested. */ - if (test_bit(SC_OP_SCANNING, &sc->sc_flags) && + if (test_bit(ATH_OP_SCANNING, &common->op_flags) && sc->spectral_mode == SPECTRAL_CHANSCAN) ath9k_spectral_scan_trigger(hw); } @@ -566,6 +566,7 @@ irqreturn_t ath_isr(int irq, void *dev) struct ath_softc *sc = dev; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; u32 sync_cause = 0; bool sched = false; @@ -575,7 +576,7 @@ irqreturn_t ath_isr(int irq, void *dev) * touch anything. Note this can happen early * on if the IRQ is shared. */ - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) + if (test_bit(ATH_OP_INVALID, &common->op_flags)) return IRQ_NONE; /* shared irq, not for us */ @@ -583,7 +584,7 @@ irqreturn_t ath_isr(int irq, void *dev) if (!ath9k_hw_intrpend(ah)) return IRQ_NONE; - if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) { + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) { ath9k_hw_kill_interrupts(ah); return IRQ_HANDLED; } @@ -684,10 +685,11 @@ int ath_reset(struct ath_softc *sc) void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); #ifdef CONFIG_ATH9K_DEBUGFS RESET_STAT_INC(sc, type); #endif - set_bit(SC_OP_HW_RESET, &sc->sc_flags); + set_bit(ATH_OP_HW_RESET, &common->op_flags); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); } @@ -768,7 +770,7 @@ static int ath9k_start(struct ieee80211_hw *hw) ath_mci_enable(sc); - clear_bit(SC_OP_INVALID, &sc->sc_flags); + clear_bit(ATH_OP_INVALID, &common->op_flags); sc->sc_ah->is_monitoring = false; if (!ath_complete_reset(sc, false)) @@ -885,7 +887,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath_cancel_work(sc); - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; @@ -940,7 +942,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_ps_restore(sc); - set_bit(SC_OP_INVALID, &sc->sc_flags); + set_bit(ATH_OP_INVALID, &common->op_flags); sc->ps_idle = prev_idle; mutex_unlock(&sc->mutex); @@ -1081,7 +1083,7 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, */ if (ah->opmode == NL80211_IFTYPE_STATION && old_opmode == NL80211_IFTYPE_AP && - test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { + test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { ieee80211_iterate_active_interfaces_atomic( sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, ath9k_sta_vif_iter, sc); @@ -1590,7 +1592,7 @@ static void ath9k_set_assoc_state(struct ath_softc *sc, struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; unsigned long flags; - set_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags); + set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); avp->primary_sta_vif = true; /* @@ -1625,8 +1627,9 @@ static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct ath_softc *sc = data; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); - if (test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) + if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) return; if (bss_conf->assoc) @@ -1657,18 +1660,18 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid, bss_conf->assoc); if (avp->primary_sta_vif && !bss_conf->assoc) { - clear_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags); + clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); avp->primary_sta_vif = false; if (ah->opmode == NL80211_IFTYPE_STATION) - clear_bit(SC_OP_BEACONS, &sc->sc_flags); + clear_bit(ATH_OP_BEACONS, &common->op_flags); } ieee80211_iterate_active_interfaces_atomic( sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, ath9k_bss_assoc_iter, sc); - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) && + if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) && ah->opmode == NL80211_IFTYPE_STATION) { memset(common->curbssid, 0, ETH_ALEN); common->curaid = 0; @@ -1897,7 +1900,7 @@ static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) return; } - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; @@ -2070,13 +2073,15 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) static void ath9k_sw_scan_start(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - set_bit(SC_OP_SCANNING, &sc->sc_flags); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + set_bit(ATH_OP_SCANNING, &common->op_flags); } static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - clear_bit(SC_OP_SCANNING, &sc->sc_flags); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + clear_bit(ATH_OP_SCANNING, &common->op_flags); } static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 71799fcade5..a0dbcc41238 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -555,7 +555,7 @@ void ath_mci_intr(struct ath_softc *sc) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; while (more_data == MCI_GPM_MORE) { - if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) return; pgpm = mci->gpm_buf.bf_addr; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 55724b02316..25304adece5 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -784,6 +784,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ath_softc *sc; struct ieee80211_hw *hw; + struct ath_common *common; u8 csz; u32 val; int ret = 0; @@ -858,9 +859,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->mem = pcim_iomap_table(pdev)[0]; sc->driver_data = id->driver_data; - /* Will be cleared in ath9k_start() */ - set_bit(SC_OP_INVALID, &sc->sc_flags); - ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); @@ -879,6 +877,10 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n", hw_name, (unsigned long)sc->mem, pdev->irq); + /* Will be cleared in ath9k_start() */ + common = ath9k_hw_common(sc->sc_ah); + set_bit(ATH_OP_INVALID, &common->op_flags); + return 0; err_init: diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index b686a749845..a65cfb91adc 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -108,7 +108,7 @@ static int ath9k_tx99_init(struct ath_softc *sc) struct ath_tx_control txctl; int r; - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_err(common, "driver is in invalid state unable to use TX99"); return -EINVAL; diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 1b3230fa365..2879887f569 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -198,7 +198,7 @@ int ath9k_suspend(struct ieee80211_hw *hw, ath_cancel_work(sc); ath_stop_ani(sc); - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); ret = -EINVAL; goto fail_wow; @@ -224,7 +224,7 @@ int ath9k_suspend(struct ieee80211_hw *hw, * STA. */ - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { + if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { ath_dbg(common, WOW, "None of the STA vifs are associated\n"); ret = 1; goto fail_wow; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index fafacfed44e..f76e6b9bb8e 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1699,7 +1699,7 @@ int ath_cabq_update(struct ath_softc *sc) ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); - qi.tqi_readyTime = (cur_conf->beacon_interval * + qi.tqi_readyTime = (TU_TO_USEC(cur_conf->beacon_interval) * ATH_CABQ_READY_TIME) / 100; ath_txq_update(sc, qnum, &qi); @@ -1769,7 +1769,7 @@ bool ath_drain_all_txq(struct ath_softc *sc) int i; u32 npend = 0; - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) + if (test_bit(ATH_OP_INVALID, &common->op_flags)) return true; ath9k_hw_abort_tx_dma(ah); @@ -1817,11 +1817,12 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) */ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_ac *ac, *last_ac; struct ath_atx_tid *tid, *last_tid; bool sent = false; - if (test_bit(SC_OP_HW_RESET, &sc->sc_flags) || + if (test_bit(ATH_OP_HW_RESET, &common->op_flags) || list_empty(&txq->axq_acq)) return; @@ -2471,7 +2472,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) ath_txq_lock(sc, txq); for (;;) { - if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) break; if (list_empty(&txq->axq_q)) { @@ -2554,7 +2555,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) int status; for (;;) { - if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) break; status = ath9k_hw_txprocdesc(ah, NULL, (void *)&ts); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 74393035706..4806a49cb61 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -179,7 +179,7 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy, int cid = wil_find_cid(wil, mac); - wil_info(wil, "%s(%pM) CID %d\n", __func__, mac, cid); + wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); if (cid < 0) return cid; @@ -218,7 +218,7 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy, return -ENOENT; memcpy(mac, wil->sta[cid].addr, ETH_ALEN); - wil_info(wil, "%s(%pM) CID %d\n", __func__, mac, cid); + wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); rc = wil_cid_fill_sinfo(wil, cid, sinfo); @@ -265,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, u16 chnl[4]; } __packed cmd; uint i, n; + int rc; if (wil->scan_request) { wil_err(wil, "Already scanning\n"); @@ -282,7 +283,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, /* FW don't support scan after connection attempt */ if (test_bit(wil_status_dontscan, &wil->status)) { - wil_err(wil, "Scan after connect attempt not supported\n"); + wil_err(wil, "Can't scan now\n"); return -EBUSY; } @@ -305,8 +306,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, request->channels[i]->center_freq); } - return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + + rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); + + if (rc) + wil->scan_request = NULL; + + return rc; } static int wil_cfg80211_connect(struct wiphy *wiphy, @@ -321,6 +327,10 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, int ch; int rc = 0; + if (test_bit(wil_status_fwconnecting, &wil->status) || + test_bit(wil_status_fwconnected, &wil->status)) + return -EALREADY; + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid, sme->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -402,10 +412,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, memcpy(conn.bssid, bss->bssid, ETH_ALEN); memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); - /* - * FW don't support scan after connection attempt - */ - set_bit(wil_status_dontscan, &wil->status); + set_bit(wil_status_fwconnecting, &wil->status); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); @@ -414,7 +421,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); } else { - clear_bit(wil_status_dontscan, &wil->status); clear_bit(wil_status_fwconnecting, &wil->status); } @@ -603,18 +609,20 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, if (wil_fix_bcon(wil, bcon)) wil_dbg_misc(wil, "Fixed bcon\n"); + mutex_lock(&wil->mutex); + rc = wil_reset(wil); if (rc) - return rc; + goto out; /* Rx VRING. */ rc = wil_rx_init(wil); if (rc) - return rc; + goto out; rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); if (rc) - return rc; + goto out; /* MAC address - pre-requisite for other commands */ wmi_set_mac_address(wil, ndev->dev_addr); @@ -638,11 +646,13 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype, channel->hw_value); if (rc) - return rc; + goto out; netif_carrier_on(ndev); +out: + mutex_unlock(&wil->mutex); return rc; } @@ -652,8 +662,11 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, int rc = 0; struct wil6210_priv *wil = wiphy_to_wil(wiphy); + mutex_lock(&wil->mutex); + rc = wmi_pcp_stop(wil); + mutex_unlock(&wil->mutex); return rc; } @@ -661,7 +674,11 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + mutex_lock(&wil->mutex); wil6210_disconnect(wil, mac); + mutex_unlock(&wil->mutex); + return 0; } diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 1d09a4b0a0f..ecdabe4adec 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -398,6 +398,44 @@ static const struct file_operations fops_reset = { .open = simple_open, }; +static void wil_seq_hexdump(struct seq_file *s, void *p, int len, + const char *prefix) +{ + char printbuf[16 * 3 + 2]; + int i = 0; + while (i < len) { + int l = min(len - i, 16); + hex_dump_to_buffer(p + i, l, 16, 1, printbuf, + sizeof(printbuf), false); + seq_printf(s, "%s%s\n", prefix, printbuf); + i += l; + } +} + +static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb) +{ + int i = 0; + int len = skb_headlen(skb); + void *p = skb->data; + int nr_frags = skb_shinfo(skb)->nr_frags; + + seq_printf(s, " len = %d\n", len); + wil_seq_hexdump(s, p, len, " : "); + + if (nr_frags) { + seq_printf(s, " nr_frags = %d\n", nr_frags); + for (i = 0; i < nr_frags; i++) { + const struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[i]; + + len = skb_frag_size(frag); + p = skb_frag_address_safe(frag); + seq_printf(s, " [%2d] : len = %d\n", i, len); + wil_seq_hexdump(s, p, len, " : "); + } + } +} + /*---------Tx/Rx descriptor------------*/ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) { @@ -438,26 +476,9 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) seq_printf(s, " SKB = %p\n", skb); if (skb) { - char printbuf[16 * 3 + 2]; - int i = 0; - int len = le16_to_cpu(d->dma.length); - void *p = skb->data; - - if (len != skb_headlen(skb)) { - seq_printf(s, "!!! len: desc = %d skb = %d\n", - len, skb_headlen(skb)); - len = min_t(int, len, skb_headlen(skb)); - } - - seq_printf(s, " len = %d\n", len); - - while (i < len) { - int l = min(len - i, 16); - hex_dump_to_buffer(p + i, l, 16, 1, printbuf, - sizeof(printbuf), false); - seq_printf(s, " : %s\n", printbuf); - i += l; - } + skb_get(skb); + wil_seq_print_skb(s, skb); + kfree_skb(skb); } seq_printf(s, "}\n"); } else { @@ -631,7 +652,8 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data) status = "connected"; break; } - seq_printf(s, "[%d] %pM %s\n", i, p->addr, status); + seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status, + (p->data_port_open ? " data_port_open" : "")); if (p->status == wil_sta_connected) { for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 10919f95a83..5824cd41e4b 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -195,8 +195,12 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { wil_dbg_irq(wil, "RX done\n"); isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; - wil_dbg_txrx(wil, "NAPI schedule\n"); - napi_schedule(&wil->napi_rx); + if (test_bit(wil_status_reset_done, &wil->status)) { + wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); + napi_schedule(&wil->napi_rx); + } else { + wil_err(wil, "Got Rx interrupt while in reset\n"); + } } if (isr) @@ -226,10 +230,15 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { wil_dbg_irq(wil, "TX done\n"); - napi_schedule(&wil->napi_tx); isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; /* clear also all VRING interrupts */ isr &= ~(BIT(25) - 1UL); + if (test_bit(wil_status_reset_done, &wil->status)) { + wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); + napi_schedule(&wil->napi_tx); + } else { + wil_err(wil, "Got Tx interrupt while in reset\n"); + } } if (isr) @@ -319,6 +328,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) if (isr & ISR_MISC_FW_ERROR) { wil_notify_fw_error(wil); isr &= ~ISR_MISC_FW_ERROR; + wil_fw_error_recovery(wil); } if (isr & ISR_MISC_MBOX_EVT) { @@ -493,6 +503,23 @@ free0: return rc; } +/* can't use wil_ioread32_and_clear because ICC value is not ser yet */ +static inline void wil_clear32(void __iomem *addr) +{ + u32 x = ioread32(addr); + + iowrite32(x, addr); +} + +void wil6210_clear_irq(struct wil6210_priv *wil) +{ + wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); +} int wil6210_init_irq(struct wil6210_priv *wil, int irq) { diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 41c362dee03..95f4efe9ef3 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -21,6 +21,10 @@ #include "wil6210.h" #include "txrx.h" +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"); + /* * Due to a hardware issue, * one has to read/write to/from NIC in 32-bit chunks; @@ -59,6 +63,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) uint i; struct wil_sta_info *sta = &wil->sta[cid]; + sta->data_port_open = false; if (sta->status != wil_sta_unused) { wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); sta->status = wil_sta_unused; @@ -112,8 +117,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) GFP_KERNEL); } clear_bit(wil_status_fwconnecting, &wil->status); - wil_dbg_misc(wil, "clear_bit(wil_status_dontscan)\n"); - clear_bit(wil_status_dontscan, &wil->status); break; default: /* AP-like interface and monitor: @@ -130,7 +133,9 @@ static void wil_disconnect_worker(struct work_struct *work) struct wil6210_priv *wil = container_of(work, struct wil6210_priv, disconnect_worker); + mutex_lock(&wil->mutex); _wil6210_disconnect(wil, NULL); + mutex_unlock(&wil->mutex); } static void wil_connect_timer_fn(ulong x) @@ -145,6 +150,38 @@ static void wil_connect_timer_fn(ulong x) schedule_work(&wil->disconnect_worker); } +static void wil_fw_error_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, fw_error_worker); + struct wireless_dev *wdev = wil->wdev; + + wil_dbg_misc(wil, "fw error worker\n"); + + if (no_fw_recovery) + return; + + mutex_lock(&wil->mutex); + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_MONITOR: + wil_info(wil, "fw error recovery started...\n"); + wil_reset(wil); + + /* need to re-allocate Rx ring after reset */ + wil_rx_init(wil); + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + /* recovery in these modes is done by upper layers */ + break; + default: + break; + } + mutex_unlock(&wil->mutex); +} + static int wil_find_free_vring(struct wil6210_priv *wil) { int i; @@ -197,6 +234,7 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_WORK(&wil->connect_worker, wil_connect_worker); INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); INIT_LIST_HEAD(&wil->pending_wmi_ev); spin_lock_init(&wil->wmi_ev_lock); @@ -223,7 +261,10 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) void wil_priv_deinit(struct wil6210_priv *wil) { cancel_work_sync(&wil->disconnect_worker); + cancel_work_sync(&wil->fw_error_worker); + mutex_lock(&wil->mutex); wil6210_disconnect(wil, NULL); + mutex_unlock(&wil->mutex); wmi_event_flush(wil); destroy_workqueue(wil->wmi_wq_conn); destroy_workqueue(wil->wmi_wq); @@ -231,40 +272,78 @@ void wil_priv_deinit(struct wil6210_priv *wil) static void wil_target_reset(struct wil6210_priv *wil) { + int delay = 0; + u32 hw_state; + u32 rev_id; + wil_dbg_misc(wil, "Resetting...\n"); + /* register read */ +#define R(a) ioread32(wil->csr + HOSTADDR(a)) /* register write */ #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) /* register set = read, OR, write */ -#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ - wil->csr + HOSTADDR(a)) +#define S(a, v) W(a, R(a) | v) + /* register clear = read, AND with inverted, write */ +#define C(a, v) W(a, R(a) & ~v) + wil->hw_version = R(RGF_USER_FW_REV_ID); + rev_id = wil->hw_version & 0xff; /* hpal_perst_from_pad_src_n_mask */ S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); /* car_perst_rst_src_n_mask */ S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); + 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(); /* order is important here */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); + wmb(); /* order is important here */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + wmb(); /* order is important here */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); - W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + if (rev_id == 1) { + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + } else { + W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); + } W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + wmb(); /* order is important here */ + + /* wait until device ready */ + do { + msleep(1); + hw_state = R(RGF_USER_HW_MACHINE_STATE); + if (delay++ > 100) { + wil_err(wil, "Reset not completed, hw_state 0x%08x\n", + hw_state); + return; + } + } while (hw_state != HW_MACHINE_BOOT_DONE); + + if (rev_id == 2) + W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); - wil_dbg_misc(wil, "Reset completed\n"); + 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); + +#undef R #undef W #undef S +#undef C } void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) @@ -299,11 +378,24 @@ int wil_reset(struct wil6210_priv *wil) { int rc; + WARN_ON(!mutex_is_locked(&wil->mutex)); + cancel_work_sync(&wil->disconnect_worker); wil6210_disconnect(wil, NULL); + wil->status = 0; /* prevent NAPI from being scheduled */ + if (test_bit(wil_status_napi_en, &wil->status)) { + napi_synchronize(&wil->napi_rx); + } + + if (wil->scan_request) { + wil_dbg_misc(wil, "Abort scan_request 0x%p\n", + wil->scan_request); + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + wil6210_disable_irq(wil); - wil->status = 0; wmi_event_flush(wil); @@ -313,6 +405,8 @@ int wil_reset(struct wil6210_priv *wil) /* TODO: put MAC in reset */ wil_target_reset(wil); + wil_rx_fini(wil); + /* init after reset */ wil->pending_connect_cid = -1; reinit_completion(&wil->wmi_ready); @@ -326,6 +420,11 @@ int wil_reset(struct wil6210_priv *wil) return rc; } +void wil_fw_error_recovery(struct wil6210_priv *wil) +{ + wil_dbg_misc(wil, "starting fw error recovery\n"); + schedule_work(&wil->fw_error_worker); +} void wil_link_on(struct wil6210_priv *wil) { @@ -353,6 +452,8 @@ static int __wil_up(struct wil6210_priv *wil) struct wireless_dev *wdev = wil->wdev; int rc; + WARN_ON(!mutex_is_locked(&wil->mutex)); + rc = wil_reset(wil); if (rc) return rc; @@ -394,6 +495,7 @@ static int __wil_up(struct wil6210_priv *wil) napi_enable(&wil->napi_rx); napi_enable(&wil->napi_tx); + set_bit(wil_status_napi_en, &wil->status); return 0; } @@ -411,6 +513,9 @@ int wil_up(struct wil6210_priv *wil) static int __wil_down(struct wil6210_priv *wil) { + WARN_ON(!mutex_is_locked(&wil->mutex)); + + clear_bit(wil_status_napi_en, &wil->status); napi_disable(&wil->napi_rx); napi_disable(&wil->napi_tx); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 717178f09aa..fdcaeb820e7 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -127,8 +127,9 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) ndev->netdev_ops = &wil_netdev_ops; ndev->ieee80211_ptr = wdev; - ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM; - ndev->features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM | + NETIF_F_SG | NETIF_F_GRO; + ndev->features |= ndev->hw_features; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index e1c8cc4a4b9..f1e1bb338d6 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -68,10 +68,14 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) goto stop_master; /* need reset here to obtain MAC */ + mutex_lock(&wil->mutex); rc = wil_reset(wil); + mutex_unlock(&wil->mutex); if (rc) goto release_irq; + wil_info(wil, "HW version: 0x%08x\n", wil->hw_version); + return 0; release_irq: @@ -149,6 +153,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, wil); wil->pdev = pdev; + wil6210_clear_irq(wil); /* FW should raise IRQ when ready */ rc = wil_if_pcie_enable(wil); if (rc) { diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 092081e209d..c8c547457eb 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -104,6 +104,23 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) return 0; } +static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d, + struct wil_ctx *ctx) +{ + dma_addr_t pa = wil_desc_addr(&d->dma.addr); + u16 dmalen = le16_to_cpu(d->dma.length); + switch (ctx->mapped_as) { + case wil_mapped_as_single: + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); + break; + case wil_mapped_as_page: + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); + break; + default: + break; + } +} + static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, int tx) { @@ -122,15 +139,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, ctx = &vring->ctx[vring->swtail]; *d = *_d; - pa = wil_desc_addr(&d->dma.addr); - dmalen = le16_to_cpu(d->dma.length); - if (vring->ctx[vring->swtail].mapped_as_page) { - dma_unmap_page(dev, pa, dmalen, - DMA_TO_DEVICE); - } else { - dma_unmap_single(dev, pa, dmalen, - DMA_TO_DEVICE); - } + wil_txdesc_unmap(dev, d, ctx); if (ctx->skb) dev_kfree_skb_any(ctx->skb); vring->swtail = wil_vring_next_tail(vring); @@ -479,7 +488,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) */ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) { - int rc; + gro_result_t rc; struct wil6210_priv *wil = ndev_to_wil(ndev); unsigned int len = skb->len; struct vring_rx_desc *d = wil_skb_rxdesc(skb); @@ -488,17 +497,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) skb_orphan(skb); - rc = netif_receive_skb(skb); + rc = napi_gro_receive(&wil->napi_rx, skb); - if (likely(rc == NET_RX_SUCCESS)) { + if (unlikely(rc == GRO_DROP)) { + ndev->stats.rx_dropped++; + stats->rx_dropped++; + wil_dbg_txrx(wil, "Rx drop %d bytes\n", len); + } else { ndev->stats.rx_packets++; stats->rx_packets++; ndev->stats.rx_bytes += len; stats->rx_bytes += len; - - } else { - ndev->stats.rx_dropped++; - stats->rx_dropped++; } } @@ -548,6 +557,11 @@ int wil_rx_init(struct wil6210_priv *wil) struct vring *vring = &wil->vring_rx; int rc; + if (vring->va) { + wil_err(wil, "Rx ring already allocated\n"); + return -EINVAL; + } + vring->size = WIL6210_RX_RING_SIZE; rc = wil_vring_alloc(wil, vring); if (rc) @@ -588,7 +602,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, .ring_size = cpu_to_le16(size), }, .ringid = id, - .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), + .cidxtid = mk_cidxtid(cid, tid), .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, .mac_ctrl = 0, .to_resolution = 0, @@ -604,6 +618,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, struct wmi_vring_cfg_done_event cmd; } __packed reply; struct vring *vring = &wil->vring_tx[id]; + struct vring_tx_data *txdata = &wil->vring_tx_data[id]; if (vring->va) { wil_err(wil, "Tx ring [%d] already allocated\n", id); @@ -611,6 +626,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, goto out; } + memset(txdata, 0, sizeof(*txdata)); vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -634,6 +650,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, } vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + txdata->enabled = 1; + return 0; out_free: wil_vring_free(wil, vring, 1); @@ -646,9 +664,16 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) { struct vring *vring = &wil->vring_tx[id]; + WARN_ON(!mutex_is_locked(&wil->mutex)); + if (!vring->va) return; + /* make sure NAPI won't touch this vring */ + wil->vring_tx_data[id].enabled = 0; + if (test_bit(wil_status_napi_en, &wil->status)) + napi_synchronize(&wil->napi_tx); + wil_vring_free(wil, vring, 1); } @@ -662,6 +687,10 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, if (cid < 0) return NULL; + if (!wil->sta[cid].data_port_open && + (skb->protocol != cpu_to_be16(ETH_P_PAE))) + return NULL; + /* TODO: fix for multiple TID */ for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) { if (wil->vring2cid_tid[i][0] == cid) { @@ -700,12 +729,19 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil, struct vring *v, *v2; struct sk_buff *skb2; int i; + u8 cid; - /* find 1-st vring */ + /* find 1-st vring eligible for data */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->vring_tx[i]; - if (v->va) - goto found; + if (!v->va) + continue; + + cid = wil->vring2cid_tid[i][0]; + if (!wil->sta[cid].data_port_open) + continue; + + goto found; } wil_err(wil, "Tx while no vrings active?\n"); @@ -721,6 +757,10 @@ found: v2 = &wil->vring_tx[i]; if (!v2->va) continue; + cid = wil->vring2cid_tid[i][0]; + if (!wil->sta[cid].data_port_open) + continue; + skb2 = skb_copy(skb, GFP_ATOMIC); if (skb2) { wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i); @@ -759,6 +799,13 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, return 0; } +static inline +void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) +{ + d->mac.d[2] |= ((nr_frags + 1) << + MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); +} + static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil, struct vring_tx_desc *d, struct sk_buff *skb) @@ -823,8 +870,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_dbg_txrx(wil, "%s()\n", __func__); - if (avail < vring->size/8) - netif_tx_stop_all_queues(wil_to_ndev(wil)); if (avail < 1 + nr_frags) { wil_err(wil, "Tx ring full. No space for %d fragments\n", 1 + nr_frags); @@ -842,6 +887,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, if (unlikely(dma_mapping_error(dev, pa))) return -EINVAL; + vring->ctx[i].mapped_as = wil_mapped_as_single; /* 1-st segment */ wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index); /* Process TCP/UDP checksum offloading */ @@ -851,8 +897,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, goto dma_error; } - d->mac.d[2] |= ((nr_frags + 1) << - MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + vring->ctx[i].nr_frags = nr_frags; + wil_tx_desc_set_nr_frags(d, nr_frags); if (nr_frags) *_d = *d; @@ -867,8 +913,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev, pa))) goto dma_error; + vring->ctx[i].mapped_as = wil_mapped_as_page; wil_tx_desc_map(d, pa, len, vring_index); - vring->ctx[i].mapped_as_page = 1; + /* no need to check return code - + * if it succeeded for 1-st descriptor, + * it will succeed here too + */ + wil_tx_desc_offload_cksum_set(wil, d, skb); *_d = *d; } /* for the last seg only */ @@ -897,7 +948,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* unmap what we have mapped */ nr_frags = f + 1; /* frags mapped + one for skb head */ for (f = 0; f < nr_frags; f++) { - u16 dmalen; struct wil_ctx *ctx; i = (swhead + f) % vring->size; @@ -905,12 +955,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, _d = &(vring->va[i].tx); *d = *_d; _d->dma.status = TX_DMA_STATUS_DU; - pa = wil_desc_addr(&d->dma.addr); - dmalen = le16_to_cpu(d->dma.length); - if (ctx->mapped_as_page) - dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); - else - dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); + wil_txdesc_unmap(dev, d, ctx); if (ctx->skb) dev_kfree_skb_any(ctx->skb); @@ -927,11 +972,15 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct wil6210_priv *wil = ndev_to_wil(ndev); struct ethhdr *eth = (void *)skb->data; struct vring *vring; + static bool pr_once_fw; int rc; wil_dbg_txrx(wil, "%s()\n", __func__); if (!test_bit(wil_status_fwready, &wil->status)) { - wil_err(wil, "FW not ready\n"); + if (!pr_once_fw) { + wil_err(wil, "FW not ready\n"); + pr_once_fw = true; + } goto drop; } if (!test_bit(wil_status_fwconnected, &wil->status)) { @@ -942,6 +991,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) wil_err(wil, "Xmit in monitor mode not supported\n"); goto drop; } + pr_once_fw = false; /* find vring */ if (is_unicast_ether_addr(eth->h_dest)) { @@ -956,6 +1006,10 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* set up vring entry */ rc = wil_tx_vring(wil, vring, skb); + /* do we still have enough room in the vring? */ + if (wil_vring_avail_tx(vring) < vring->size/8) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + switch (rc) { case 0: /* statistics will be updated on the tx_complete */ @@ -985,69 +1039,82 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) struct net_device *ndev = wil_to_ndev(wil); struct device *dev = wil_to_dev(wil); struct vring *vring = &wil->vring_tx[ringid]; + struct vring_tx_data *txdata = &wil->vring_tx_data[ringid]; int done = 0; int cid = wil->vring2cid_tid[ringid][0]; struct wil_net_stats *stats = &wil->sta[cid].stats; + volatile struct vring_tx_desc *_d; if (!vring->va) { wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); return 0; } + if (!txdata->enabled) { + wil_info(wil, "Tx irq[%d]: vring disabled\n", ringid); + return 0; + } + wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); while (!wil_vring_is_empty(vring)) { - volatile struct vring_tx_desc *_d = - &vring->va[vring->swtail].tx; - struct vring_tx_desc dd, *d = ⅆ - dma_addr_t pa; - u16 dmalen; + int new_swtail; struct wil_ctx *ctx = &vring->ctx[vring->swtail]; - struct sk_buff *skb = ctx->skb; - - *d = *_d; + /** + * For the fragmented skb, HW will set DU bit only for the + * last fragment. look for it + */ + int lf = (vring->swtail + ctx->nr_frags) % vring->size; + /* TODO: check we are not past head */ - if (!(d->dma.status & TX_DMA_STATUS_DU)) + _d = &vring->va[lf].tx; + if (!(_d->dma.status & TX_DMA_STATUS_DU)) break; - dmalen = le16_to_cpu(d->dma.length); - trace_wil6210_tx_done(ringid, vring->swtail, dmalen, - d->dma.error); - wil_dbg_txrx(wil, - "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", - vring->swtail, dmalen, d->dma.status, - d->dma.error); - wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, - (const void *)d, sizeof(*d), false); - - pa = wil_desc_addr(&d->dma.addr); - if (ctx->mapped_as_page) - dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); - else - dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); - - if (skb) { - if (d->dma.error == 0) { - ndev->stats.tx_packets++; - stats->tx_packets++; - ndev->stats.tx_bytes += skb->len; - stats->tx_bytes += skb->len; - } else { - ndev->stats.tx_errors++; - stats->tx_errors++; - } + new_swtail = (lf + 1) % vring->size; + while (vring->swtail != new_swtail) { + struct vring_tx_desc dd, *d = ⅆ + u16 dmalen; + struct wil_ctx *ctx = &vring->ctx[vring->swtail]; + struct sk_buff *skb = ctx->skb; + _d = &vring->va[vring->swtail].tx; + + *d = *_d; - dev_kfree_skb_any(skb); + dmalen = le16_to_cpu(d->dma.length); + trace_wil6210_tx_done(ringid, vring->swtail, dmalen, + d->dma.error); + wil_dbg_txrx(wil, + "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", + vring->swtail, dmalen, d->dma.status, + d->dma.error); + wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + wil_txdesc_unmap(dev, d, ctx); + + if (skb) { + if (d->dma.error == 0) { + ndev->stats.tx_packets++; + stats->tx_packets++; + ndev->stats.tx_bytes += skb->len; + stats->tx_bytes += skb->len; + } else { + ndev->stats.tx_errors++; + stats->tx_errors++; + } + + dev_kfree_skb_any(skb); + } + memset(ctx, 0, sizeof(*ctx)); + /* There is no need to touch HW descriptor: + * - ststus bit TX_DMA_STATUS_DU is set by design, + * so hardware will not try to process this desc., + * - rest of descriptor will be initialized on Tx. + */ + vring->swtail = wil_vring_next_tail(vring); + done++; } - memset(ctx, 0, sizeof(*ctx)); - /* - * There is no need to touch HW descriptor: - * - ststus bit TX_DMA_STATUS_DU is set by design, - * so hardware will not try to process this desc., - * - rest of descriptor will be initialized on Tx. - */ - vring->swtail = wil_vring_next_tail(vring); - done++; } if (wil_vring_avail_tx(vring) > vring->size/4) netif_tx_wake_all_queues(wil_to_ndev(wil)); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 980dccc82b3..2a2dec75f02 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -74,23 +74,21 @@ struct RGF_ICR { } __packed; /* registers - FW addresses */ -#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) -#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ - #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) -#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) -#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_HW_MACHINE_STATE (0x8801dc) + #define HW_MACHINE_BOOT_DONE (0x3fffffd) #define RGF_USER_USER_CPU_0 (0x8801e0) +#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) +#define RGF_USER_FW_REV_ID (0x880a8c) /* chip revision */ +#define RGF_USER_CLKS_CTL_0 (0x880abc) + #define BIT_USER_CLKS_RST_PWGD BIT(11) /* reset on "power good" */ #define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) #define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) #define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) #define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) - -#define RGF_DMA_PSEUDO_CAUSE (0x881c68) -#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) -#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) - #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) - #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) - #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) +#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ + #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) #define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) @@ -105,13 +103,22 @@ struct RGF_ICR { /* Interrupt moderation control */ #define RGF_DMA_ITR_CNT_TRSH (0x881c5c) #define RGF_DMA_ITR_CNT_DATA (0x881c60) -#define RGF_DMA_ITR_CNT_CRL (0x881C64) +#define RGF_DMA_ITR_CNT_CRL (0x881c64) #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) +#define RGF_DMA_PSEUDO_CAUSE (0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) + #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) + #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) + #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) + +#define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4) + /* popular locations */ #define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ @@ -125,6 +132,31 @@ struct RGF_ICR { /* Hardware definitions end */ +/** + * mk_cidxtid - construct @cidxtid field + * @cid: CID value + * @tid: TID value + * + * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID + */ +static inline u8 mk_cidxtid(u8 cid, u8 tid) +{ + return ((tid & 0xf) << 4) | (cid & 0xf); +} + +/** + * parse_cidxtid - parse @cidxtid field + * @cid: store CID value here + * @tid: store TID value here + * + * @cidxtid field encoded as bits 0..3 - CID; 4..7 - TID + */ +static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid) +{ + *cid = cidxtid & 0xf; + *tid = (cidxtid >> 4) & 0xf; +} + struct wil6210_mbox_ring { u32 base; u16 entry_size; /* max. size of mbox entry, incl. all headers */ @@ -184,12 +216,19 @@ struct pending_wmi_event { } __packed event; }; +enum { /* for wil_ctx.mapped_as */ + wil_mapped_as_none = 0, + wil_mapped_as_single = 1, + wil_mapped_as_page = 2, +}; + /** * struct wil_ctx - software context for Vring descriptor */ struct wil_ctx { struct sk_buff *skb; - u8 mapped_as_page:1; + u8 nr_frags; + u8 mapped_as; }; union vring_desc; @@ -204,6 +243,14 @@ struct vring { struct wil_ctx *ctx; /* ctx[size] - software context */ }; +/** + * Additional data for Tx Vring + */ +struct vring_tx_data { + int enabled; + +}; + enum { /* for wil6210_priv.status */ wil_status_fwready = 0, wil_status_fwconnecting, @@ -211,6 +258,7 @@ enum { /* for wil6210_priv.status */ wil_status_dontscan, wil_status_reset_done, wil_status_irqen, /* FIXME: interrupts enabled - for debug */ + wil_status_napi_en, /* NAPI enabled protected by wil->mutex */ }; struct pci_dev; @@ -296,6 +344,7 @@ struct wil_sta_info { u8 addr[ETH_ALEN]; enum wil_sta_status status; struct wil_net_stats stats; + bool data_port_open; /* can send any data, not only EAPOL */ /* Rx BACK */ struct wil_tid_ampdu_rx *tid_rx[WIL_STA_TID_NUM]; unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)]; @@ -309,6 +358,7 @@ struct wil6210_priv { void __iomem *csr; ulong status; u32 fw_version; + u32 hw_version; u8 n_mids; /* number of additional MIDs as reported by FW */ /* profile */ u32 monitor_flags; @@ -329,6 +379,7 @@ struct wil6210_priv { struct workqueue_struct *wmi_wq_conn; /* for connect worker */ struct work_struct connect_worker; struct work_struct disconnect_worker; + struct work_struct fw_error_worker; /* for FW error recovery */ struct timer_list connect_timer; int pending_connect_cid; struct list_head pending_wmi_ev; @@ -343,6 +394,7 @@ struct wil6210_priv { /* DMA related */ struct vring vring_rx; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; + struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS]; u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; /* scan */ @@ -406,6 +458,7 @@ void wil_if_remove(struct wil6210_priv *wil); int wil_priv_init(struct wil6210_priv *wil); void wil_priv_deinit(struct wil6210_priv *wil); int wil_reset(struct wil6210_priv *wil); +void wil_fw_error_recovery(struct wil6210_priv *wil); void wil_link_on(struct wil6210_priv *wil); void wil_link_off(struct wil6210_priv *wil); int wil_up(struct wil6210_priv *wil); @@ -439,6 +492,7 @@ int wmi_rxon(struct wil6210_priv *wil, bool on); int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason); +void wil6210_clear_irq(struct wil6210_priv *wil); int wil6210_init_irq(struct wil6210_priv *wil, int irq); void wil6210_fini_irq(struct wil6210_priv *wil, int irq); void wil6210_disable_irq(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 24eed096358..2ba56eef0c4 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -462,7 +462,9 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, wil->sinfo_gen++; + mutex_lock(&wil->mutex); wil6210_disconnect(wil, evt->bssid); + mutex_unlock(&wil->mutex); } static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) @@ -550,9 +552,16 @@ static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len) { struct net_device *ndev = wil_to_ndev(wil); struct wmi_data_port_open_event *evt = d; + u8 cid = evt->cid; - wil_dbg_wmi(wil, "Link UP for CID %d\n", evt->cid); + wil_dbg_wmi(wil, "Link UP for CID %d\n", cid); + if (cid >= ARRAY_SIZE(wil->sta)) { + wil_err(wil, "Link UP for invalid CID %d\n", cid); + return; + } + + wil->sta[cid].data_port_open = true; netif_carrier_on(ndev); } @@ -560,10 +569,17 @@ static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len) { struct net_device *ndev = wil_to_ndev(wil); struct wmi_wbe_link_down_event *evt = d; + u8 cid = evt->cid; wil_dbg_wmi(wil, "Link DOWN for CID %d, reason %d\n", - evt->cid, le32_to_cpu(evt->reason)); + cid, le32_to_cpu(evt->reason)); + + if (cid >= ARRAY_SIZE(wil->sta)) { + wil_err(wil, "Link DOWN for invalid CID %d\n", cid); + return; + } + wil->sta[cid].data_port_open = false; netif_carrier_off(ndev); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 4a6508e7e3a..a16e644e7c0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -269,26 +269,17 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, break; } - if (ret) { - /* - * SleepCSR register access can fail when - * waking up the device so reduce this noise - * in the logs. - */ - if (addr != SBSDIO_FUNC1_SLEEPCSR) - brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - else - brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - } + if (ret) + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", fn, addr, ret); + return ret; } static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { - u8 func_num; + u8 func; s32 retry = 0; int ret; @@ -302,9 +293,9 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, * The rest: function 1 silicon backplane core registers */ if ((addr & ~REG_F0_REG_MASK) == 0) - func_num = SDIO_FUNC_0; + func = SDIO_FUNC_0; else - func_num = SDIO_FUNC_1; + func = SDIO_FUNC_1; do { if (!write) @@ -312,16 +303,26 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, /* for retry wait for 1 ms till bus get settled down */ if (retry) usleep_range(1000, 2000); - ret = brcmf_sdiod_request_data(sdiodev, func_num, addr, regsz, + ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz, data, write); } while (ret != 0 && ret != -ENOMEDIUM && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret == -ENOMEDIUM) brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); - else if (ret != 0) - brcmf_err("failed with %d\n", ret); - + else if (ret != 0) { + /* + * SleepCSR register access can fail when + * waking up the device so reduce this noise + * in the logs. + */ + if (addr != SBSDIO_FUNC1_SLEEPCSR) + brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + else + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + } return ret; } @@ -988,6 +989,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335_4339)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)}, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -1153,7 +1155,7 @@ static struct sdio_driver brcmf_sdmmc_driver = { }, }; -static int brcmf_sdio_pd_probe(struct platform_device *pdev) +static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) { brcmf_dbg(SDIO, "Enter\n"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index a07b95ef9e7..df130ef53d1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -504,6 +504,7 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) ci->pub.ramsize = 0x3c000; break; case BCM4339_CHIP_ID: + case BCM4354_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; @@ -1006,6 +1007,10 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) chip = container_of(pub, struct brcmf_chip_priv, pub); switch (pub->chip) { + case BCM4354_CHIP_ID: + /* explicitly check SR engine enable bit */ + pmu_cc3_mask = BIT(2); + /* fall-through */ case BCM43241_CHIP_ID: case BCM4335_CHIP_ID: case BCM4339_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a111b6fbbeb..47a6f3957b7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -175,6 +175,7 @@ struct rte_console { #define SBSDIO_ALP_AVAIL 0x40 /* Status: HT is ready */ #define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_CSR_MASK 0x1F #define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) #define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) #define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) @@ -458,10 +459,11 @@ struct brcmf_sdio { bool alp_only; /* Don't use HT clock (ALP only) */ u8 *ctrl_frame_buf; - u32 ctrl_frame_len; + u16 ctrl_frame_len; bool ctrl_frame_stat; - spinlock_t txqlock; + spinlock_t txq_lock; /* protect bus->txq */ + struct semaphore tx_seq_lock; /* protect bus->tx_seq */ wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; @@ -578,6 +580,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43143_NVRAM_NAME); @@ -597,6 +601,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); struct brcmf_firmware_names { u32 chipid; @@ -622,7 +628,8 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) } + { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; @@ -714,16 +721,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) int err = 0; int try_cnt = 0; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter: on=%d\n", on); wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); - if (err) { - brcmf_err("SDIO_AOS KSO write error: %d\n", err); - return err; - } if (on) { /* device WAKEUP through KSO: @@ -753,13 +756,19 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) &err); if (((rd_val & bmask) == cmp_val) && !err) break; - brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n", - try_cnt, MAX_KSO_ATTEMPTS, err); + udelay(KSO_WAIT_US); brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); } while (try_cnt++ < MAX_KSO_ATTEMPTS); + if (try_cnt > 2) + brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, + rd_val, err); + + if (try_cnt > MAX_KSO_ATTEMPTS) + brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); + return err; } @@ -960,6 +969,7 @@ static int brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) { int err = 0; + u8 clkcsr; brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), @@ -978,8 +988,20 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) - return -EBUSY; + data_ok(bus))) { + err = -EBUSY; + goto done; + } + + clkcsr = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if ((clkcsr & SBSDIO_CSR_MASK) == 0) { + brcmf_dbg(SDIO, "no clock, set ALP\n"); + brcmf_sdiod_regwb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + } err = brcmf_sdio_kso_control(bus, false); /* disable watchdog */ if (!err) @@ -996,7 +1018,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_err("error while changing bus sleep state %d\n", err); - return err; + goto done; } } @@ -1008,7 +1030,8 @@ end: } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; } @@ -2311,13 +2334,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { pkt_num = 1; - __skb_queue_head_init(&pktq); + if (down_interruptible(&bus->tx_seq_lock)) + return cnt; if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, bus->sdiodev->txglomsz); pkt_num = min_t(u32, pkt_num, brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); - spin_lock_bh(&bus->txqlock); + __skb_queue_head_init(&pktq); + spin_lock_bh(&bus->txq_lock); for (i = 0; i < pkt_num; i++) { pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); @@ -2325,11 +2350,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) break; __skb_queue_tail(&pktq, pkt); } - spin_unlock_bh(&bus->txqlock); - if (i == 0) + spin_unlock_bh(&bus->txq_lock); + if (i == 0) { + up(&bus->tx_seq_lock); break; + } ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + up(&bus->tx_seq_lock); + cnt += i; /* In poll mode, need to check for other events */ @@ -2358,6 +2387,68 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) return cnt; } +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ + u8 doff; + u16 pad; + uint retries = 0; + struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Back the pointer to make room for bus header */ + frame -= bus->tx_hdrlen; + len += bus->tx_hdrlen; + + /* Add alignment padding (optional for ctl frames) */ + doff = ((unsigned long)frame % bus->head_align); + if (doff) { + frame -= doff; + len += doff; + memset(frame + bus->tx_hdrlen, 0, doff); + } + + /* Round send length to next SDIO block */ + pad = 0; + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + pad = bus->blocksize - (len % bus->blocksize); + if ((pad > bus->roundup) || (pad >= bus->blocksize)) + pad = 0; + } else if (len % bus->head_align) { + pad = bus->head_align - (len % bus->head_align); + } + len += pad; + + hd_info.len = len - pad; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff + bus->tx_hdrlen; + hd_info.seq_num = bus->tx_seq; + hd_info.lastfrm = true; + hd_info.tail_pad = pad; + brcmf_sdio_hdpack(bus, frame, &hd_info); + + if (bus->txglom) + brcmf_sdio_update_hwhdr(frame, len); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + frame, len, "Tx Frame:\n"); + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && + BRCMF_HDRS_ON(), + frame, min_t(u16, len, 16), "TxHdr:\n"); + + do { + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + } while (ret < 0 && retries++ < TXRETRIES); + + return ret; +} + static void brcmf_sdio_bus_stop(struct device *dev) { u32 local_hostintmask; @@ -2591,26 +2682,23 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); - if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, - (u32)bus->ctrl_frame_len); - - if (err < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && + (down_interruptible(&bus->tx_seq_lock) == 0)) { + if (data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); - sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + up(&bus->tx_seq_lock); } /* Send queued frames (limit 1 if rx may still be pending) */ - else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit - && data_ok(bus)) { + if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && + data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; brcmf_sdio_sendfromq(bus, framecnt); @@ -2644,7 +2732,6 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - ulong flags; brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); @@ -2660,7 +2747,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->sdcnt.fcqueued++; /* Priority based enq */ - spin_lock_irqsave(&bus->txqlock, flags); + spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ *(u16 *)(pkt->cb) = 0; if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { @@ -2675,7 +2762,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } - spin_unlock_irqrestore(&bus->txqlock, flags); + spin_unlock_bh(&bus->txq_lock); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) @@ -2770,87 +2857,27 @@ break2: } #endif /* DEBUG */ -static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - int ret; - - bus->ctrl_frame_stat = false; - ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - - if (ret < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - - return ret; -} - static int brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { - u8 *frame; - u16 len, pad; - uint retries = 0; - u8 doff = 0; - int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret = -1; brcmf_dbg(TRACE, "Enter\n"); - /* Back the pointer to make a room for bus header */ - frame = msg - bus->tx_hdrlen; - len = (msglen += bus->tx_hdrlen); - - /* Add alignment padding (optional for ctl frames) */ - doff = ((unsigned long)frame % bus->head_align); - if (doff) { - frame -= doff; - len += doff; - msglen += doff; - memset(frame, 0, doff + bus->tx_hdrlen); - } - /* precondition: doff < bus->head_align */ - doff += bus->tx_hdrlen; - - /* Round send length to next SDIO block */ - pad = 0; - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - pad = bus->blocksize - (len % bus->blocksize); - if ((pad > bus->roundup) || (pad >= bus->blocksize)) - pad = 0; - } else if (len % bus->head_align) { - pad = bus->head_align - (len % bus->head_align); - } - len += pad; - - /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - - /* Make sure backplane clock is on */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - sdio_release_host(bus->sdiodev->func[1]); - - hd_info.len = (u16)msglen; - hd_info.channel = SDPCM_CONTROL_CHANNEL; - hd_info.dat_offset = doff; - hd_info.seq_num = bus->tx_seq; - hd_info.lastfrm = true; - hd_info.tail_pad = pad; - brcmf_sdio_hdpack(bus, frame, &hd_info); - - if (bus->txglom) - brcmf_sdio_update_hwhdr(frame, len); + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; if (!data_ok(bus)) { brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", bus->tx_max, bus->tx_seq); - bus->ctrl_frame_stat = true; + up(&bus->tx_seq_lock); /* Send from dpc */ - bus->ctrl_frame_buf = frame; - bus->ctrl_frame_len = len; + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, @@ -2861,22 +2888,18 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) ret = 0; } else { brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); + bus->ctrl_frame_stat = false; + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; ret = -1; } } - if (ret == -1) { - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - do { - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdio_tx_frame(bus, frame, len); - sdio_release_host(bus->sdiodev->func[1]); - } while (ret < 0 && retries++ < TXRETRIES); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); + sdio_release_host(bus->sdiodev->func[1]); + up(&bus->tx_seq_lock); } if (ret) @@ -3971,6 +3994,7 @@ brcmf_sdio_watchdog_thread(void *data) brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); } else break; } @@ -4047,7 +4071,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) } spin_lock_init(&bus->rxctl_lock); - spin_lock_init(&bus->txqlock); + spin_lock_init(&bus->txq_lock); + sema_init(&bus->tx_seq_lock, 1); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index af17a5bc8b8..614e4888504 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -48,6 +48,11 @@ #define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ +/* OBSS Coex Auto/On/Off */ +#define BRCMF_OBSS_COEX_AUTO (-1) +#define BRCMF_OBSS_COEX_OFF 0 +#define BRCMF_OBSS_COEX_ON 1 + enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_CLIENT, BRCMF_FIL_P2P_IF_GO, @@ -87,6 +92,11 @@ struct brcmf_fil_bss_enable_le { __le32 enable; }; +struct brcmf_fil_bwcap_le { + __le32 band; + __le32 bw_cap; +}; + /** * struct tdls_iovar - common structure for tdls iovars. * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index fc4f98b275d..f3445ac627e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -797,7 +797,8 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, /* SOCIAL CHANNELS 1, 6, 11 */ search_state = WL_P2P_DISC_ST_SEARCH; brcmf_dbg(INFO, "P2P SEARCH PHASE START\n"); - } else if (dev != NULL && vif->mode == WL_MODE_AP) { + } else if (dev != NULL && + vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { /* If you are already a GO, then do SEARCH only */ brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n"); search_state = WL_P2P_DISC_ST_SEARCH; @@ -2256,7 +2257,6 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct brcmf_cfg80211_vif *vif; enum brcmf_fil_p2p_if_types iftype; - enum wl_mode mode; int err; if (brcmf_cfg80211_vif_event_armed(cfg)) @@ -2267,11 +2267,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, switch (type) { case NL80211_IFTYPE_P2P_CLIENT: iftype = BRCMF_FIL_P2P_IF_CLIENT; - mode = WL_MODE_BSS; break; case NL80211_IFTYPE_P2P_GO: iftype = BRCMF_FIL_P2P_IF_GO; - mode = WL_MODE_AP; break; case NL80211_IFTYPE_P2P_DEVICE: return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 00bd1e16c3c..e0e649aab8d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -191,6 +191,7 @@ static struct ieee80211_supported_band __wl_band_2ghz = { .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size, + .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true}, }; static struct ieee80211_supported_band __wl_band_5ghz_a = { @@ -494,6 +495,19 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) return err; } +static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) +{ + enum nl80211_iftype iftype; + + iftype = vif->wdev.iftype; + return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; +} + +static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) +{ + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; +} + static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, enum nl80211_iftype type, @@ -654,7 +668,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, type); return -EOPNOTSUPP; case NL80211_IFTYPE_ADHOC: - vif->mode = WL_MODE_IBSS; infra = 0; break; case NL80211_IFTYPE_STATION: @@ -670,12 +683,10 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, */ return 0; } - vif->mode = WL_MODE_BSS; infra = 1; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - vif->mode = WL_MODE_AP; ap = 1; break; default: @@ -699,7 +710,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, err = -EAGAIN; goto done; } - brcmf_dbg(INFO, "IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ? + brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? "Adhoc" : "Infra"); } ndev->ieee80211_ptr->iftype = type; @@ -1682,22 +1693,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); - /*increase dwell time to receive probe response or detect Beacon - * from target AP at a noisy air only during connect command - */ - ext_join_params->scan_le.active_time = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); - ext_join_params->scan_le.passive_time = - cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* Set up join scan parameters */ ext_join_params->scan_le.scan_type = -1; - /* to sync with presence period of VSDB GO. - * Send probe request more frequently. Probe request will be stopped - * when it gets probe response from target AP/GO. - */ - ext_join_params->scan_le.nprobes = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / - BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); ext_join_params->scan_le.home_time = cpu_to_le32(-1); if (sme->bssid) @@ -1710,6 +1708,25 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->assoc_le.chanspec_list[0] = cpu_to_le16(chanspec); + /* Increase dwell time to receive probe response or detect + * beacon from target AP at a noisy air only during connect + * command. + */ + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* To sync with presence period of VSDB GO send probe request + * more frequently. Probe request will be stopped when it gets + * probe response from target AP/GO. + */ + ext_join_params->scan_le.nprobes = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / + BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); + } else { + ext_join_params->scan_le.active_time = cpu_to_le32(-1); + ext_join_params->scan_le.passive_time = cpu_to_le32(-1); + ext_join_params->scan_le.nprobes = cpu_to_le32(-1); } err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, @@ -1917,7 +1934,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "Setting the key index %d\n", key.index); memcpy(key.data, params->key, key.len); - if ((ifp->vif->mode != WL_MODE_AP) && + if (!brcmf_is_apmode(ifp->vif) && (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); @@ -2016,7 +2033,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: - if (ifp->vif->mode != WL_MODE_AP) { + if (!brcmf_is_apmode(ifp->vif)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); @@ -2177,7 +2194,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (ifp->vif->mode == WL_MODE_AP) { + if (brcmf_is_apmode(ifp->vif)) { memcpy(&sta_info_le, mac, ETH_ALEN); err = brcmf_fil_iovar_data_get(ifp, "sta_info", &sta_info_le, @@ -2194,7 +2211,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, } brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", sinfo->inactive_time, sinfo->connected_time); - } else if (ifp->vif->mode == WL_MODE_BSS) { + } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) { if (memcmp(mac, bssid, ETH_ALEN)) { brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n", mac, bssid); @@ -2476,11 +2493,6 @@ CleanUp: return err; } -static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) -{ - return vif->mode == WL_MODE_IBSS; -} - static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { @@ -4253,32 +4265,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode) }; -static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type) -{ - switch (type) { - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return -ENOTSUPP; - case NL80211_IFTYPE_ADHOC: - return WL_MODE_IBSS; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - return WL_MODE_BSS; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - return WL_MODE_AP; - case NL80211_IFTYPE_P2P_DEVICE: - return WL_MODE_P2P; - case NL80211_IFTYPE_UNSPECIFIED: - default: - break; - } - - return -EINVAL; -} - static void brcmf_wiphy_pno_params(struct wiphy *wiphy) { /* scheduled scan settings */ @@ -4403,7 +4389,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->wdev.wiphy = cfg->wiphy; vif->wdev.iftype = type; - vif->mode = brcmf_nl80211_iftype_to_mode(type); vif->pm_block = pm_block; vif->roam_off = -1; @@ -4697,7 +4682,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, s32 err = 0; u16 reason; - if (ifp->vif->mode == WL_MODE_AP) { + if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(e)) { brcmf_dbg(CONN, "Linkup\n"); @@ -4945,6 +4930,30 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } +static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) +{ + struct brcmf_fil_bwcap_le band_bwcap; + u32 val; + int err; + + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_40MHZ_BIT); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } + return err; +} + struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { @@ -5002,6 +5011,17 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto cfg80211_p2p_attach_out; } + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + err = brcmf_enable_bw40_2g(ifp); + if (!err) + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + } + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 5715bb0708c..283c525a44f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -89,21 +89,6 @@ enum brcmf_scan_status { BRCMF_SCAN_STATUS_SUPPRESS, }; -/** - * enum wl_mode - driver mode of virtual interface. - * - * @WL_MODE_BSS: connects to BSS. - * @WL_MODE_IBSS: operate as ad-hoc. - * @WL_MODE_AP: operate as access-point. - * @WL_MODE_P2P: provide P2P discovery. - */ -enum wl_mode { - WL_MODE_BSS, - WL_MODE_IBSS, - WL_MODE_AP, - WL_MODE_P2P -}; - /* dongle configuration */ struct brcmf_cfg80211_conf { u32 frag_threshold; @@ -193,7 +178,6 @@ struct vif_saved_ie { * @ifp: lower layer interface pointer * @wdev: wireless device. * @profile: profile information. - * @mode: operating mode. * @roam_off: roaming state. * @sme_state: SME state using enum brcmf_vif_status bits. * @pm_block: power-management blocked. @@ -204,7 +188,6 @@ struct brcmf_cfg80211_vif { struct brcmf_if *ifp; struct wireless_dev wdev; struct brcmf_cfg80211_profile profile; - s32 mode; s32 roam_off; unsigned long sme_state; bool pm_block; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 925034b80e9..8c5fa4e5813 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -426,6 +426,12 @@ static int brcms_ops_start(struct ieee80211_hw *hw) bool blocked; int err; + if (!wl->ucode.bcm43xx_bomminor) { + err = brcms_request_fw(wl, wl->wlc->hw->d11core); + if (err) + return -ENOENT; + } + ieee80211_wake_queues(hw); spin_lock_bh(&wl->lock); blocked = brcms_rfkill_set_hw_state(wl); @@ -433,14 +439,6 @@ static int brcms_ops_start(struct ieee80211_hw *hw) if (!blocked) wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); - if (!wl->ucode.bcm43xx_bomminor) { - err = brcms_request_fw(wl, wl->wlc->hw->d11core); - if (err) { - brcms_remove(wl->wlc->hw->d11core); - return -ENOENT; - } - } - spin_lock_bh(&wl->lock); /* avoid acknowledging frames before a non-monitor device is added */ wl->mute_tx = true; @@ -1094,12 +1092,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw) * Attach to the WL device identified by vendor and device parameters. * regs is a host accessible memory address pointing to WL device registers. * - * brcms_attach is not defined as static because in the case where no bus - * is defined, wl_attach will never be called, and thus, gcc will issue - * a warning that this function is defined but not used if we declare - * it as static. - * - * * is called in brcms_bcma_probe() context, therefore no locking required. */ static struct brcms_info *brcms_attach(struct bcma_device *pdev) diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 6fa5d486378..d816270db3b 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -43,5 +43,6 @@ #define BCM4335_CHIP_ID 0x4335 #define BCM43362_CHIP_ID 43362 #define BCM4339_CHIP_ID 0x4339 +#define BCM4354_CHIP_ID 0x4354 #endif /* _BRCM_HW_IDS_H_ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index ba1b1ea5425..6a6df71af1d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -252,13 +252,17 @@ static void iwl_bg_bt_runtime_config(struct work_struct *work) struct iwl_priv *priv = container_of(work, struct iwl_priv, bt_runtime_config); + mutex_lock(&priv->mutex); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; + goto out; /* dont send host command if rf-kill is on */ if (!iwl_is_ready_rf(priv)) - return; + goto out; + iwlagn_send_advance_bt_config(priv); +out: + mutex_unlock(&priv->mutex); } static void iwl_bg_bt_full_concurrency(struct work_struct *work) @@ -2035,7 +2039,7 @@ static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) ieee80211_free_txskb(priv->hw, skb); } -static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); @@ -2045,6 +2049,8 @@ static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) clear_bit(STATUS_RF_KILL_HW, &priv->status); wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); + + return false; } static const struct iwl_op_mode_ops iwl_dvm_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index fbd262ffa49..003a546571d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -134,6 +134,7 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, }; const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { @@ -145,6 +146,7 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .high_temp = true, .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, }; const struct iwl_cfg iwl7260_2n_cfg = { @@ -155,6 +157,7 @@ const struct iwl_cfg iwl7260_2n_cfg = { .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, }; const struct iwl_cfg iwl7260_n_cfg = { @@ -165,6 +168,7 @@ const struct iwl_cfg iwl7260_n_cfg = { .nvm_ver = IWL7260_NVM_VERSION, .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, }; const struct iwl_cfg iwl3160_2ac_cfg = { diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 13ec56607d1..3f17dc3f2c8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -262,6 +262,7 @@ struct iwl_cfg { bool high_temp; bool d0i3; u8 nvm_hw_section_num; + bool lp_xtal_workaround; const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index f13dec9ad9c..fe129c94ae3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -139,6 +139,13 @@ #define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) /* + * CSR HW resources monitor registers + */ +#define CSR_MONITOR_CFG_REG (CSR_BASE+0x214) +#define CSR_MONITOR_STATUS_REG (CSR_BASE+0x228) +#define CSR_MONITOR_XTAL_RESOURCES (0x00000010) + +/* * CSR Hardware Revision Workaround Register. Indicates hardware rev; * "step" determines CCK backoff for txpower calculation. Used for 4965 only. * See also CSR_HW_REV register. @@ -173,6 +180,7 @@ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ +#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ #define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ #define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ @@ -240,6 +248,7 @@ * 001 -- MAC power-down * 010 -- PHY (radio) power-down * 011 -- Error + * 10: XTAL ON request * 9-6: SYS_CONFIG * Indicates current system configuration, reflecting pins on chip * as forced high/low by device circuit board. @@ -271,6 +280,7 @@ #define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) #define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) #define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) +#define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400) #define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) @@ -396,6 +406,34 @@ #define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) /* + * SHR target access (Shared block memory space) + * + * Shared internal registers can be accessed directly from PCI bus through SHR + * arbiter without need for the MAC HW to be powered up. This is possible due to + * indirect read/write via HEEP_CTRL_WRD_PCIEX_CTRL (0xEC) and + * HEEP_CTRL_WRD_PCIEX_DATA (0xF4) registers. + * + * Use iwl_write32()/iwl_read32() family to access these registers. The MAC HW + * need not be powered up so no "grab inc access" is required. + */ + +/* + * Registers for accessing shared registers (e.g. SHR_APMG_GP1, + * SHR_APMG_XTAL_CFG). For example, to read from SHR_APMG_GP1 register (0x1DC), + * first, write to the control register: + * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register) + * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 2 (read access) + * second, read from the data register HEEP_CTRL_WRD_PCIEX_DATA[31:0]. + * + * To write the register, first, write to the data register + * HEEP_CTRL_WRD_PCIEX_DATA[31:0] and then: + * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register) + * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 3 (write access) + */ +#define HEEP_CTRL_WRD_PCIEX_CTRL_REG (CSR_BASE+0x0ec) +#define HEEP_CTRL_WRD_PCIEX_DATA_REG (CSR_BASE+0x0f4) + +/* * HBUS (Host-side Bus) * * HBUS registers are mapped directly into PCI bus space, but are used diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index f04ff871dc6..d14f19339d6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -125,6 +125,22 @@ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30), }; +/** + * enum iwl_ucode_tlv_api - ucode api + * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. + */ +enum iwl_ucode_tlv_api { + IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), +}; + +/** + * enum iwl_ucode_tlv_capa - ucode capabilities + * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 + */ +enum iwl_ucode_tlv_capa { + IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), +}; + /* The default calibrate table size if not specified by firmware file */ #define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 #define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19 diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 07372f2b025..44cc3cf4576 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -93,14 +93,14 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, } IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); -static inline u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) +u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) { u32 val = iwl_trans_read_prph(trans, ofs); trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val); return val; } -static inline void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) +void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) { trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val); iwl_trans_write_prph(trans, ofs, val); diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 9e81b23d738..665ddd9dbbc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -70,7 +70,9 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value); +u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs); u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs); +void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout); diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 2f962ec0b75..6be30c69850 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -299,9 +299,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, - struct ieee80211_sta_vht_cap *vht_cap) + struct ieee80211_sta_vht_cap *vht_cap, + u8 tx_chains, u8 rx_chains) { - int num_ants = num_of_ant(data->valid_rx_ant); + int num_rx_ants = num_of_ant(rx_chains); + int num_tx_ants = num_of_ant(tx_chains); vht_cap->vht_supported = true; @@ -311,8 +313,10 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; - if (num_ants > 1) + if (num_tx_ants > 1) vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; + else + vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; if (iwlwifi_mod_params.amsdu_size_8K) vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; @@ -327,10 +331,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); - if (num_ants == 1 || - cfg->rx_with_siso_diversity) { - vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | - IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) { + vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; /* this works because NOT_SUPPORTED == 3 */ vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); @@ -375,7 +377,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, tx_chains, rx_chains); if (enable_vht) - iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); + iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, + tx_chains, rx_chains); if (n_channels != n_used) IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 5d78207040b..ea29504ac61 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -119,7 +119,8 @@ struct iwl_cfg; * @queue_not_full: notifies that a HW queue is not full any more. * Must be atomic and called with BH disabled. * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that - * the radio is killed. May sleep. + * the radio is killed. Return %true if the device should be stopped by + * the transport immediately after the call. May sleep. * @free_skb: allows the transport layer to free skbs that haven't been * reclaimed by the op_mode. This can happen when the driver is freed and * there are Tx packets pending in the transport layer. @@ -144,7 +145,7 @@ struct iwl_op_mode_ops { struct iwl_device_cmd *cmd); void (*queue_full)(struct iwl_op_mode *op_mode, int queue); void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); - void (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); + bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb); void (*nic_error)(struct iwl_op_mode *op_mode); void (*cmd_queue_full)(struct iwl_op_mode *op_mode); @@ -195,11 +196,11 @@ static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode, op_mode->ops->queue_not_full(op_mode, queue); } -static inline void iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, - bool state) +static inline bool __must_check +iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state) { might_sleep(); - op_mode->ops->hw_rf_kill(op_mode, state); + return op_mode->ops->hw_rf_kill(op_mode, state); } static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode, diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 9c90186d174..5f657c50140 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -95,7 +95,8 @@ #define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ #define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) -#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) +#define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200) +#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) #define APMG_RTC_INT_STT_RFKILL (0x10000000) @@ -105,6 +106,26 @@ /* Device NMI register */ #define DEVICE_SET_NMI_REG 0x00a01c30 +/* Shared registers (0x0..0x3ff, via target indirect or periphery */ +#define SHR_BASE 0x00a10000 + +/* Shared GP1 register */ +#define SHR_APMG_GP1_REG 0x01dc +#define SHR_APMG_GP1_REG_PRPH (SHR_BASE + SHR_APMG_GP1_REG) +#define SHR_APMG_GP1_WF_XTAL_LP_EN 0x00000004 +#define SHR_APMG_GP1_CHICKEN_BIT_SELECT 0x80000000 + +/* Shared DL_CFG register */ +#define SHR_APMG_DL_CFG_REG 0x01c4 +#define SHR_APMG_DL_CFG_REG_PRPH (SHR_BASE + SHR_APMG_DL_CFG_REG) +#define SHR_APMG_DL_CFG_RTCS_CLK_SELECTOR_MSK 0x000000c0 +#define SHR_APMG_DL_CFG_RTCS_CLK_INTERNAL_XTAL 0x00000080 +#define SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP 0x00000100 + +/* Shared APMG_XTAL_CFG register */ +#define SHR_APMG_XTAL_CFG_REG 0x1c0 +#define SHR_APMG_XTAL_CFG_XTAL_ON_REQ 0x80000000 + /* * Device reset for family 8000 * write to bit 24 in order to reset the CPU diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 41d390fd2ac..ccdd3b7c4cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -2,8 +2,8 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o iwlmvm-y += scan.o time-event.o rs.o -iwlmvm-y += power.o bt-coex.o -iwlmvm-y += led.o tt.o +iwlmvm-y += power.o coex.o +iwlmvm-y += led.o tt.o offloading.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 2aa3ee93c68..685f7e8e694 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -61,9 +61,11 @@ * *****************************************************************************/ +#include <linux/ieee80211.h> +#include <linux/etherdevice.h> #include <net/mac80211.h> -#include "fw-api-bt-coex.h" +#include "fw-api-coex.h" #include "iwl-modparams.h" #include "mvm.h" #include "iwl-debug.h" @@ -305,6 +307,215 @@ static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = { cpu_to_le32(0x33113311), }; +struct corunning_block_luts { + u8 range; + __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; +}; + +/* + * Ranges for the antenna coupling calibration / co-running block LUT: + * LUT0: [ 0, 12[ + * LUT1: [12, 20[ + * LUT2: [20, 21[ + * LUT3: [21, 23[ + * LUT4: [23, 27[ + * LUT5: [27, 30[ + * LUT6: [30, 32[ + * LUT7: [32, 33[ + * LUT8: [33, - [ + */ +static const struct corunning_block_luts antenna_coupling_ranges[] = { + { + .range = 0, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 12, + .lut20 = { + cpu_to_le32(0x00000001), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 20, + .lut20 = { + cpu_to_le32(0x00000002), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 21, + .lut20 = { + cpu_to_le32(0x00000003), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 23, + .lut20 = { + cpu_to_le32(0x00000004), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 27, + .lut20 = { + cpu_to_le32(0x00000005), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 30, + .lut20 = { + cpu_to_le32(0x00000006), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 32, + .lut20 = { + cpu_to_le32(0x00000007), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 33, + .lut20 = { + cpu_to_le32(0x00000008), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, +}; + static enum iwl_bt_coex_lut_type iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) { @@ -390,8 +601,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) BT_VALID_LUT | BT_VALID_WIFI_RX_SW_PRIO_BOOST | BT_VALID_WIFI_TX_SW_PRIO_BOOST | - BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40 | BT_VALID_ANT_ISOLATION | BT_VALID_ANT_ISOLATION_THRS | BT_VALID_TXTX_DELTA_FREQ_THRS | @@ -401,6 +610,17 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) if (IWL_MVM_BT_COEX_SYNC2SCO) bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); + if (IWL_MVM_BT_COEX_CORUNNING) { + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); + bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); + } + + if (IWL_MVM_BT_COEX_MPLUT) { + bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); + } + if (mvm->cfg->bt_shared_single_ant) memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, sizeof(iwl_single_shared_ant)); @@ -408,6 +628,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, sizeof(iwl_combined_lookup)); + /* Take first Co-running block LUT to get started */ + memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20, + sizeof(bt_cmd->bt4_corun_lut20)); + memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20, + sizeof(bt_cmd->bt4_corun_lut40)); + memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, sizeof(iwl_bt_prio_boost)); memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut, @@ -498,7 +724,7 @@ int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) struct iwl_host_cmd cmd = { .id = BT_CONFIG, .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_DUP, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, .flags = CMD_ASYNC, }; struct iwl_mvm_sta *mvmsta; @@ -952,8 +1178,8 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) #define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) -u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) +u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); enum iwl_bt_coex_lut_type lut_type; @@ -989,6 +1215,38 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT; } +u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *info, u8 ac) +{ + __le16 fc = hdr->frame_control; + + if (info->band != IEEE80211_BAND_2GHZ) + return 0; + + if (unlikely(mvm->bt_tx_prio)) + return mvm->bt_tx_prio - 1; + + /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ + if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || + is_multicast_ether_addr(hdr->addr1) || + ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) || + ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) + return 3; + + switch (ac) { + case IEEE80211_AC_BE: + return 1; + case IEEE80211_AC_VO: + return 3; + case IEEE80211_AC_VI: + return 2; + default: + break; + } + + return 0; +} + void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) { if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX)) @@ -996,3 +1254,69 @@ void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) iwl_mvm_bt_coex_notif_handle(mvm); } + +int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *dev_cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 ant_isolation = le32_to_cpup((void *)pkt->data); + u8 __maybe_unused lower_bound, upper_bound; + u8 lut; + + struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + .flags = CMD_SYNC, + }; + + if (!IWL_MVM_BT_COEX_CORUNNING) + return 0; + + lockdep_assert_held(&mvm->mutex); + + if (ant_isolation == mvm->last_ant_isol) + return 0; + + for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) + if (ant_isolation < antenna_coupling_ranges[lut + 1].range) + break; + + lower_bound = antenna_coupling_ranges[lut].range; + + if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) + upper_bound = antenna_coupling_ranges[lut + 1].range; + else + upper_bound = antenna_coupling_ranges[lut].range; + + IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", + ant_isolation, lower_bound, upper_bound, lut); + + mvm->last_ant_isol = ant_isolation; + + if (mvm->last_corun_lut == lut) + return 0; + + mvm->last_corun_lut = lut; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return 0; + cmd.data[0] = bt_cmd; + + bt_cmd->flags = cpu_to_le32(BT_COEX_NW); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); + + /* For the moment, use the same LUT for 20GHz and 40GHz */ + memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20, + sizeof(bt_cmd->bt4_corun_lut20)); + + memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20, + sizeof(bt_cmd->bt4_corun_lut40)); + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 2d133b1b2dd..51685693af2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -79,8 +79,8 @@ #define IWL_MVM_PS_SNOOZE_WINDOW 50 #define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 #define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64 -#define IWL_MVM_LOWLAT_SINGLE_BINDING_MAXDUR 24 /* TU */ -#define IWL_MVM_LOWLAT_DUAL_BINDING_MAXDUR 24 /* TU */ #define IWL_MVM_BT_COEX_SYNC2SCO 1 +#define IWL_MVM_BT_COEX_CORUNNING 1 +#define IWL_MVM_BT_COEX_MPLUT 1 #endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index b956e2f0b63..e56f5a0edf8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -376,139 +376,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, return err; } -static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - union { - struct iwl_proto_offload_cmd_v1 v1; - struct iwl_proto_offload_cmd_v2 v2; - struct iwl_proto_offload_cmd_v3_small v3s; - struct iwl_proto_offload_cmd_v3_large v3l; - } cmd = {}; - struct iwl_host_cmd hcmd = { - .id = PROT_OFFLOAD_CONFIG_CMD, - .flags = CMD_SYNC, - .data[0] = &cmd, - .dataflags[0] = IWL_HCMD_DFL_DUP, - }; - struct iwl_proto_offload_cmd_common *common; - u32 enabled = 0, size; - u32 capa_flags = mvm->fw->ucode_capa.flags; -#if IS_ENABLED(CONFIG_IPV6) - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int i; - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || - capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { - struct iwl_ns_config *nsc; - struct iwl_targ_addr *addrs; - int n_nsc, n_addrs; - int c; - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { - nsc = cmd.v3s.ns_config; - n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; - addrs = cmd.v3s.targ_addrs; - n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; - } else { - nsc = cmd.v3l.ns_config; - n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; - addrs = cmd.v3l.targ_addrs; - n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; - } - - if (mvmvif->num_target_ipv6_addrs) - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - - /* - * For each address we have (and that will fit) fill a target - * address struct and combine for NS offload structs with the - * solicited node addresses. - */ - for (i = 0, c = 0; - i < mvmvif->num_target_ipv6_addrs && - i < n_addrs && c < n_nsc; i++) { - struct in6_addr solicited_addr; - int j; - - addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], - &solicited_addr); - for (j = 0; j < c; j++) - if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, - &solicited_addr) == 0) - break; - if (j == c) - c++; - addrs[i].addr = mvmvif->target_ipv6_addrs[i]; - addrs[i].config_num = cpu_to_le32(j); - nsc[j].dest_ipv6_addr = solicited_addr; - memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); - } - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) - cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); - else - cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); - } - - BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != - sizeof(mvmvif->target_ipv6_addrs[0])); - - for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) - memcpy(cmd.v2.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.v2.target_ipv6_addr[i])); - } else { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); - } - - BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != - sizeof(mvmvif->target_ipv6_addrs[0])); - - for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) - memcpy(cmd.v1.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.v1.target_ipv6_addr[i])); - } -#endif - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { - common = &cmd.v3s.common; - size = sizeof(cmd.v3s); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { - common = &cmd.v3l.common; - size = sizeof(cmd.v3l); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - common = &cmd.v2.common; - size = sizeof(cmd.v2); - } else { - common = &cmd.v1.common; - size = sizeof(cmd.v1); - } - - if (vif->bss_conf.arp_addr_cnt) { - enabled |= IWL_D3_PROTO_OFFLOAD_ARP; - common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; - memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); - } - - if (!enabled) - return 0; - - common->enabled = cpu_to_le32(enabled); - - hcmd.len[0] = size; - return iwl_mvm_send_cmd(mvm, &hcmd); -} - enum iwl_mvm_tcp_packet_type { MVM_TCP_TX_SYN, MVM_TCP_RX_SYNACK, @@ -846,8 +713,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, quota_cmd.quotas[0].id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, mvmvif->phy_ctxt->color)); - quota_cmd.quotas[0].quota = cpu_to_le32(100); - quota_cmd.quotas[0].max_duration = cpu_to_le32(1000); + quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA); + quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); for (i = 1; i < MAX_BINDINGS; i++) quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); @@ -927,6 +794,20 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) IWL_ERR(mvm, "failed to set non-QoS seqno\n"); } +static int +iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm, + const struct iwl_wowlan_config_cmd_v3 *cmd) +{ + /* start only with the v2 part of the command */ + u16 cmd_len = sizeof(cmd->common); + + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID) + cmd_len = sizeof(*cmd); + + return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC, + cmd_len, cmd); +} + static int __iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan, bool test) @@ -939,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, struct iwl_mvm_vif *mvmvif; struct ieee80211_sta *ap_sta; struct iwl_mvm_sta *mvm_ap_sta; - struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; + struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {}; struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; struct iwl_d3_manager_config d3_cfg_cmd_data = { @@ -961,7 +842,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, .tkip = &tkip_cmd, .use_tkip = false, }; - int ret, i; + int ret; int len __maybe_unused; if (!wowlan) { @@ -1002,49 +883,41 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; - /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */ + /* TODO: wowlan_config_cmd.common.wowlan_ba_teardown_tids */ - wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported; + wowlan_config_cmd.common.is_11n_connection = + ap_sta->ht_cap.ht_supported; /* Query the last used seqno and set it */ ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); if (ret < 0) goto out_noreset; - wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret); + wowlan_config_cmd.common.non_qos_seq = cpu_to_le16(ret); - /* - * For QoS counters, we store the one to use next, so subtract 0x10 - * since the uCode will add 0x10 *before* using the value while we - * increment after using the value (i.e. store the next value to use). - */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = mvm_ap_sta->tid_data[i].seq_number; - seq -= 0x10; - wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq); - } + iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd.common); if (wowlan->disconnect) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | IWL_WOWLAN_WAKEUP_LINK_CHANGE); if (wowlan->magic_pkt) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); if (wowlan->gtk_rekey_failure) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); if (wowlan->eap_identity_req) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); if (wowlan->four_way_handshake) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); if (wowlan->n_patterns) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); if (wowlan->rfkill_release) - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); if (wowlan->tcp) { @@ -1052,7 +925,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, * Set the "link change" (really "link lost") flag as well * since that implies losing the TCP connection. */ - wowlan_config_cmd.wakeup_filter |= + wowlan_config_cmd.common.wakeup_filter |= cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | @@ -1150,9 +1023,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, } } - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, - CMD_SYNC, sizeof(wowlan_config_cmd), - &wowlan_config_cmd); + ret = iwl_mvm_send_wowlan_config_cmd(mvm, &wowlan_config_cmd); if (ret) goto out; @@ -1160,7 +1031,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; - ret = iwl_mvm_send_proto_offload(mvm, vif); + ret = iwl_mvm_send_proto_offload(mvm, vif, false, CMD_SYNC); if (ret) goto out; diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index f64e972191e..9b59e1d7ae7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -312,6 +312,11 @@ static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif, mutex_lock(&mvm->mutex); mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id); + if (IS_ERR_OR_NULL(mvmsta)) { + mutex_unlock(&mvm->mutex); + return -ENOTCONN; + } + mvmsta->bt_reduced_txpower_dbg = false; ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, reduced_tx_power); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index e0ff43ed248..1b52deea608 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -60,11 +60,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +#include <linux/vmalloc.h> + #include "mvm.h" #include "sta.h" #include "iwl-io.h" #include "iwl-prph.h" #include "debugfs.h" +#include "fw-error-dump.h" static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) @@ -117,6 +120,51 @@ static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, return ret; } +static int iwl_dbgfs_fw_error_dump_open(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int ret; + + if (!mvm) + return -EINVAL; + + mutex_lock(&mvm->mutex); + if (!mvm->fw_error_dump) { + ret = -ENODATA; + goto out; + } + + file->private_data = mvm->fw_error_dump; + mvm->fw_error_dump = NULL; + kfree(mvm->fw_error_sram); + mvm->fw_error_sram = NULL; + mvm->fw_error_sram_len = 0; + ret = 0; + +out: + mutex_unlock(&mvm->mutex); + return ret; +} + +static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_fw_error_dump_file *dump_file = file->private_data; + + return simple_read_from_buffer(user_buf, count, ppos, + dump_file, + le32_to_cpu(dump_file->file_len)); +} + +static int iwl_dbgfs_fw_error_dump_release(struct inode *inode, + struct file *file) +{ + vfree(file->private_data); + + return 0; +} + static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -350,6 +398,9 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, le32_to_cpu(notif->secondary_ch_lut)); pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n", le32_to_cpu(notif->bt_activity_grading)); + pos += scnprintf(buf+pos, bufsz-pos, + "antenna isolation = %d CORUN LUT index = %d\n", + mvm->last_ant_isol, mvm->last_corun_lut); mutex_unlock(&mvm->mutex); @@ -392,6 +443,22 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } +static ssize_t +iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + u32 bt_tx_prio; + + if (sscanf(buf, "%u", &bt_tx_prio) != 1) + return -EINVAL; + if (bt_tx_prio > 4) + return -EINVAL; + + mvm->bt_tx_prio = bt_tx_prio; + + return count; +} + #define PRINT_STATS_LE32(_str, _val) \ pos += scnprintf(buf + pos, bufsz - pos, \ fmt_table, _str, \ @@ -536,56 +603,60 @@ static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm, loff_t *ppos, struct iwl_mvm_frame_stats *stats) { - char *buff; - int pos = 0, idx, i; + char *buff, *pos, *endpos; + int idx, i; int ret; - size_t bufsz = 1024; + static const size_t bufsz = 1024; buff = kmalloc(bufsz, GFP_KERNEL); if (!buff) return -ENOMEM; spin_lock_bh(&mvm->drv_stats_lock); - pos += scnprintf(buff + pos, bufsz - pos, + + pos = buff; + endpos = pos + bufsz; + + pos += scnprintf(pos, endpos - pos, "Legacy/HT/VHT\t:\t%d/%d/%d\n", stats->legacy_frames, stats->ht_frames, stats->vht_frames); - pos += scnprintf(buff + pos, bufsz - pos, "20/40/80\t:\t%d/%d/%d\n", + pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n", stats->bw_20_frames, stats->bw_40_frames, stats->bw_80_frames); - pos += scnprintf(buff + pos, bufsz - pos, "NGI/SGI\t\t:\t%d/%d\n", + pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n", stats->ngi_frames, stats->sgi_frames); - pos += scnprintf(buff + pos, bufsz - pos, "SISO/MIMO2\t:\t%d/%d\n", + pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n", stats->siso_frames, stats->mimo2_frames); - pos += scnprintf(buff + pos, bufsz - pos, "FAIL/SCSS\t:\t%d/%d\n", + pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n", stats->fail_frames, stats->success_frames); - pos += scnprintf(buff + pos, bufsz - pos, "MPDUs agg\t:\t%d\n", + pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n", stats->agg_frames); - pos += scnprintf(buff + pos, bufsz - pos, "A-MPDUs\t\t:\t%d\n", + pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n", stats->ampdu_count); - pos += scnprintf(buff + pos, bufsz - pos, "Avg MPDUs/A-MPDU:\t%d\n", + pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n", stats->ampdu_count > 0 ? (stats->agg_frames / stats->ampdu_count) : 0); - pos += scnprintf(buff + pos, bufsz - pos, "Last Rates\n"); + pos += scnprintf(pos, endpos - pos, "Last Rates\n"); idx = stats->last_frame_idx - 1; for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) { idx = (idx + 1) % ARRAY_SIZE(stats->last_rates); if (stats->last_rates[idx] == 0) continue; - pos += scnprintf(buff + pos, bufsz - pos, "Rate[%d]: ", + pos += scnprintf(pos, endpos - pos, "Rate[%d]: ", (int)(ARRAY_SIZE(stats->last_rates) - i)); - pos += rs_pretty_print_rate(buff + pos, stats->last_rates[idx]); + pos += rs_pretty_print_rate(pos, stats->last_rates[idx]); } spin_unlock_bh(&mvm->drv_stats_lock); - ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos); + ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); kfree(buff); return ret; @@ -1032,9 +1103,16 @@ MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); +static const struct file_operations iwl_dbgfs_fw_error_dump_ops = { + .open = iwl_dbgfs_fw_error_dump_open, + .read = iwl_dbgfs_fw_error_dump_read, + .release = iwl_dbgfs_fw_error_dump_release, +}; + #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); @@ -1049,12 +1127,15 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) struct dentry *bcast_dir __maybe_unused; char buf[100]; + spin_lock_init(&mvm->drv_stats_lock); + mvm->debugfs_dir = 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(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); MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD) @@ -1064,6 +1145,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 20b723d6270..21877e5966a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -77,6 +77,8 @@ * @BT_COEX_3W: * @BT_COEX_NW: * @BT_COEX_SYNC2SCO: + * @BT_COEX_CORUNNING: + * @BT_COEX_MPLUT: * * The COEX_MODE must be set for each command. Even if it is not changed. */ @@ -88,6 +90,8 @@ enum iwl_bt_coex_flags { BT_COEX_3W = 0x2 << BT_COEX_MODE_POS, BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, BT_COEX_SYNC2SCO = BIT(7), + BT_COEX_CORUNNING = BIT(8), + BT_COEX_MPLUT = BIT(9), }; /* diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 521997669c9..10fcc1a79eb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -239,7 +239,7 @@ enum iwl_wowlan_wakeup_filters { IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ -struct iwl_wowlan_config_cmd { +struct iwl_wowlan_config_cmd_v2 { __le32 wakeup_filter; __le16 non_qos_seq; __le16 qos_seq[8]; @@ -247,6 +247,12 @@ struct iwl_wowlan_config_cmd { u8 is_11n_connection; } __packed; /* WOWLAN_CONFIG_API_S_VER_2 */ +struct iwl_wowlan_config_cmd_v3 { + struct iwl_wowlan_config_cmd_v2 common; + u8 offloading_tid; + u8 reserved[3]; +} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */ + /* * WOWLAN_TSC_RSC_PARAMS */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index b674c2a2b51..8e122f3a7a7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -76,6 +76,8 @@ * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) + * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored + * on old firmwares). * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command @@ -107,6 +109,7 @@ enum iwl_tx_flags { TX_CMD_FLG_VHT_NDPA = BIT(8), TX_CMD_FLG_HT_NDPA = BIT(9), TX_CMD_FLG_CSI_FDBK2HOST = BIT(10), + TX_CMD_FLG_BT_PRIO_POS = 11, TX_CMD_FLG_BT_DIS = BIT(12), TX_CMD_FLG_SEQ_CTL = BIT(13), TX_CMD_FLG_MORE_FRAG = BIT(14), diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 807fa525caf..6e75b52588d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -70,7 +70,7 @@ #include "fw-api-mac.h" #include "fw-api-power.h" #include "fw-api-d3.h" -#include "fw-api-bt-coex.h" +#include "fw-api-coex.h" /* maximal number of Tx queues in any platform */ #define IWL_MVM_MAX_QUEUES 20 @@ -95,6 +95,7 @@ enum { /* PHY context commands */ PHY_CONTEXT_CMD = 0x8, DBG_CFG = 0x9, + ANTENNA_COUPLING_NOTIFICATION = 0xa, /* station table */ ADD_STA_KEY = 0x17, diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h b/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h new file mode 100644 index 00000000000..58c8941c0d9 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * 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 Corporation. All rights reserved. + * + * 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 <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. All rights reserved. + * 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 __fw_error_dump_h__ +#define __fw_error_dump_h__ + +#include <linux/types.h> + +#define IWL_FW_ERROR_DUMP_BARKER 0x14789632 + +/** + * enum iwl_fw_error_dump_type - types of data in the dump file + * @IWL_FW_ERROR_DUMP_SRAM: + * @IWL_FW_ERROR_DUMP_REG: + */ +enum iwl_fw_error_dump_type { + IWL_FW_ERROR_DUMP_SRAM = 0, + IWL_FW_ERROR_DUMP_REG = 1, + + IWL_FW_ERROR_DUMP_MAX, +}; + +/** + * struct iwl_fw_error_dump_data - data for one type + * @type: %enum iwl_fw_error_dump_type + * @len: the length starting from %data - must be a multiplier of 4. + * @data: the data itself padded to be a multiplier of 4. + */ +struct iwl_fw_error_dump_data { + __le32 type; + __le32 len; + __u8 data[]; +} __packed __aligned(4); + +/** + * struct iwl_fw_error_dump_file - the layout of the header of the file + * @barker: must be %IWL_FW_ERROR_DUMP_BARKER + * @file_len: the length of all the file starting from %barker + * @data: array of %struct iwl_fw_error_dump_data + */ +struct iwl_fw_error_dump_file { + __le32 barker; + __le32 file_len; + u8 data[0]; +} __packed __aligned(4); + +#endif /* __fw_error_dump_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c index 6b4ea6bf8ff..e3b3cf4dbd7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/iwlwifi/mvm/led.c @@ -94,6 +94,8 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm) int ret; switch (mode) { + case IWL_LED_BLINK: + IWL_ERR(mvm, "Blink led mode not supported, used default\n"); case IWL_LED_DEFAULT: case IWL_LED_RF_STATE: mode = IWL_LED_RF_STATE; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index c2ab6a3318c..4dd9ff43b8b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -205,7 +205,7 @@ static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = { void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) { - if (!mvm->trans->cfg->d0i3) + if (!iwl_mvm_is_d0i3_supported(mvm)) return; IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); @@ -215,7 +215,7 @@ void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) { - if (!mvm->trans->cfg->d0i3) + if (!iwl_mvm_is_d0i3_supported(mvm)) return; IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); @@ -228,7 +228,7 @@ iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref) { int i; - if (!mvm->trans->cfg->d0i3) + if (!iwl_mvm_is_d0i3_supported(mvm)) return; for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) { @@ -295,7 +295,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) !iwlwifi_mod_params.sw_crypto) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) { + if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) { hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; hw->uapsd_queues = IWL_UAPSD_AC_INFO; hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; @@ -365,7 +365,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) else hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) { + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; @@ -375,8 +375,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) } hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | - NL80211_FEATURE_P2P_GO_OPPPS | - NL80211_FEATURE_LOW_PRIORITY_SCAN; + NL80211_FEATURE_P2P_GO_OPPPS; mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; @@ -424,6 +423,47 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) return ret; } +static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct iwl_mvm_sta *mvmsta; + bool defer = false; + + /* + * double check the IN_D0I3 flag both before and after + * taking the spinlock, in order to prevent taking + * the spinlock when not needed. + */ + if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))) + return false; + + spin_lock(&mvm->d0i3_tx_lock); + /* + * testing the flag again ensures the skb dequeue + * loop (on d0i3 exit) hasn't run yet. + */ + if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) + goto out; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (mvmsta->sta_id == IWL_MVM_STATION_COUNT || + mvmsta->sta_id != mvm->d0i3_ap_sta_id) + goto out; + + __skb_queue_tail(&mvm->d0i3_tx, skb); + ieee80211_stop_queues(mvm->hw); + + /* trigger wakeup */ + iwl_mvm_ref(mvm, IWL_MVM_REF_TX); + iwl_mvm_unref(mvm, IWL_MVM_REF_TX); + + defer = true; +out: + spin_unlock(&mvm->d0i3_tx_lock); + return defer; +} + static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) @@ -451,6 +491,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, sta = NULL; if (sta) { + if (iwl_mvm_defer_tx(mvm, sta, skb)) + return; if (iwl_mvm_tx_skb(mvm, skb, sta)) goto drop; return; @@ -489,6 +531,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; + bool tx_agg_ref = false; IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", sta->addr, tid, action); @@ -496,6 +539,23 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, if (!(mvm->nvm_data->sku_cap_11n_enable)) return -EACCES; + /* return from D0i3 before starting a new Tx aggregation */ + if (action == IEEE80211_AMPDU_TX_START) { + iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG); + tx_agg_ref = true; + + /* + * wait synchronously until D0i3 exit to get the correct + * sequence number for the tid + */ + if (!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) { + WARN_ON_ONCE(1); + iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); + return -EIO; + } + } + mutex_lock(&mvm->mutex); switch (action) { @@ -533,6 +593,13 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, } mutex_unlock(&mvm->mutex); + /* + * If the tid is marked as started, we won't use it for offloaded + * traffic on the next D0i3 entry. It's safe to unref. + */ + if (tx_agg_ref) + iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); + return ret; } @@ -557,6 +624,15 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { +#ifdef CONFIG_IWLWIFI_DEBUGFS + static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL }; + + iwl_mvm_fw_error_dump(mvm); + + /* notify the userspace about the error we had */ + kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env); +#endif + iwl_trans_stop_device(mvm->trans); mvm->scan_status = IWL_MVM_SCAN_NONE; @@ -610,6 +686,7 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw) mutex_lock(&mvm->mutex); clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + iwl_mvm_d0i3_enable_tx(mvm, NULL); ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", @@ -1255,6 +1332,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC)); } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) { ret = iwl_mvm_power_update_mac(mvm, vif); @@ -1437,8 +1515,6 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_notification_wait wait_scan_done; - static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, }; int ret; if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS) @@ -1448,22 +1524,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, switch (mvm->scan_status) { case IWL_MVM_SCAN_SCHED: - iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done, - scan_done_notif, - ARRAY_SIZE(scan_done_notif), - NULL, NULL); - iwl_mvm_sched_scan_stop(mvm); - ret = iwl_wait_notification(&mvm->notif_wait, - &wait_scan_done, HZ); + ret = iwl_mvm_sched_scan_stop(mvm); if (ret) { ret = -EBUSY; goto out; } - /* iwl_mvm_rx_scan_offload_complete_notif() will be called - * soon but will not reset the scan status as it won't be - * IWL_MVM_SCAN_SCHED any more since we queue the next scan - * immediately (below) - */ break; case IWL_MVM_SCAN_NONE: break; @@ -1479,7 +1544,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); out: mutex_unlock(&mvm->mutex); - + /* make sure to flush the Rx handler before the next scan arrives */ + iwl_mvm_wait_for_async_handlers(mvm); return ret; } @@ -1641,7 +1707,9 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { /* enable beacon filtering */ - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC)); + if (vif->bss_conf.dtim_period) + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, + CMD_SYNC)); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { @@ -1738,9 +1806,26 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); - if (mvm->scan_status != IWL_MVM_SCAN_NONE) { - IWL_DEBUG_SCAN(mvm, - "SCHED SCAN request during internal scan - abort\n"); + switch (mvm->scan_status) { + case IWL_MVM_SCAN_OS: + IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n"); + ret = iwl_mvm_cancel_scan(mvm); + if (ret) { + ret = -EBUSY; + goto out; + } + + /* + * iwl_mvm_rx_scan_complete() will be called soon but will + * not reset the scan status as it won't be IWL_MVM_SCAN_OS + * any more since we queue the next scan immediately (below). + * We make sure it is called before the next scan starts by + * flushing the async-handlers work. + */ + break; + case IWL_MVM_SCAN_NONE: + break; + default: ret = -EBUSY; goto out; } @@ -1762,6 +1847,8 @@ err: mvm->scan_status = IWL_MVM_SCAN_NONE; out: mutex_unlock(&mvm->mutex); + /* make sure to flush the Rx handler before the next scan arrives */ + iwl_mvm_wait_for_async_handlers(mvm); return ret; } @@ -1769,12 +1856,14 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; mutex_lock(&mvm->mutex); - iwl_mvm_sched_scan_stop(mvm); + ret = iwl_mvm_sched_scan_stop(mvm); mutex_unlock(&mvm->mutex); + iwl_mvm_wait_for_async_handlers(mvm); - return 0; + return ret; } static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 302cf779c17..d564233a65d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -230,6 +230,8 @@ enum iwl_mvm_ref_type { IWL_MVM_REF_P2P_CLIENT, IWL_MVM_REF_AP_IBSS, IWL_MVM_REF_USER, + IWL_MVM_REF_TX, + IWL_MVM_REF_TX_AGG, IWL_MVM_REF_COUNT, }; @@ -317,13 +319,13 @@ struct iwl_mvm_vif { bool seqno_valid; u16 seqno; +#endif #if IS_ENABLED(CONFIG_IPV6) /* IPv6 addresses for WoWLAN */ struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; int num_target_ipv6_addrs; #endif -#endif #ifdef CONFIG_IWLWIFI_DEBUGFS struct iwl_mvm *mvm; @@ -346,6 +348,8 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) return (void *)vif->drv_priv; } +extern const u8 tid_to_mac80211_ac[]; + enum iwl_scan_status { IWL_MVM_SCAN_NONE, IWL_MVM_SCAN_OS, @@ -571,6 +575,9 @@ struct iwl_mvm { /* -1 for always, 0 for never, >0 for that many times */ s8 restart_fw; + void *fw_error_dump; + void *fw_error_sram; + u32 fw_error_sram_len; struct led_classdev led; @@ -591,12 +598,20 @@ struct iwl_mvm { /* d0i3 */ u8 d0i3_ap_sta_id; + bool d0i3_offloading; struct work_struct d0i3_exit_work; + struct sk_buff_head d0i3_tx; + /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */ + spinlock_t d0i3_tx_lock; + wait_queue_head_t d0i3_exit_waitq; /* BT-Coex */ u8 bt_kill_msk; struct iwl_bt_coex_profile_notif last_bt_notif; struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; + u32 last_ant_isol; + u8 last_corun_lut; + u8 bt_tx_prio; /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; @@ -630,6 +645,7 @@ enum iwl_mvm_status { IWL_MVM_STATUS_HW_CTKILL, IWL_MVM_STATUS_ROC_RUNNING, IWL_MVM_STATUS_IN_HW_RESTART, + IWL_MVM_STATUS_IN_D0I3, }; static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) @@ -656,6 +672,12 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) return iwl_mvm_sta_from_mac80211(sta); } +static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) +{ + return mvm->trans->cfg->d0i3 && + (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); +} + extern const u8 iwl_mvm_ac_to_tx_fifo[]; struct iwl_rate_info { @@ -680,7 +702,10 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, struct ieee80211_tx_rate *r); u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); -void iwl_mvm_dump_sram(struct iwl_mvm *mvm); +#ifdef CONFIG_IWLWIFI_DEBUGFS +void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); +void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm); +#endif u8 first_antenna(u8 mask); u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); @@ -706,6 +731,11 @@ static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; } int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync); void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm); +static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) +{ + flush_work(&mvm->async_handlers_wk); +} + /* Statistics */ int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, @@ -739,6 +769,9 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, @@ -793,7 +826,7 @@ int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); -void iwl_mvm_cancel_scan(struct iwl_mvm *mvm); +int iwl_mvm_cancel_scan(struct iwl_mvm *mvm); /* Scheduled scan */ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, @@ -807,7 +840,7 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); -void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm); +int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm); int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); @@ -878,10 +911,17 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { } #endif +void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, + struct iwl_wowlan_config_cmd_v2 *cmd); +int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool disable_offloading, + u32 cmd_flags); /* D0i3 */ void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); +void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); /* BT Coex */ int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); @@ -892,10 +932,12 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); -u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); +u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta); +u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *info, u8 ac); int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable); enum iwl_bt_kill_msk { @@ -942,6 +984,8 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* Low latency */ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool value); +/* get SystemLowLatencyMode - only needed for beacon threshold? */ +bool iwl_mvm_low_latency(struct iwl_mvm *mvm); /* get VMACLowLatencyMode */ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) { diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c new file mode 100644 index 00000000000..9bfb95e89cf --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/offloading.c @@ -0,0 +1,215 @@ +/****************************************************************************** + * + * 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) 2012 - 2014 Intel Corporation. All rights reserved. + * + * 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 <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * 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. + * + *****************************************************************************/ +#include <net/ipv6.h> +#include <net/addrconf.h> +#include "mvm.h" + +void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, + struct iwl_wowlan_config_cmd_v2 *cmd) +{ + int i; + + /* + * For QoS counters, we store the one to use next, so subtract 0x10 + * since the uCode will add 0x10 *before* using the value while we + * increment after using the value (i.e. store the next value to use). + */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = mvm_ap_sta->tid_data[i].seq_number; + seq -= 0x10; + cmd->qos_seq[i] = cpu_to_le16(seq); + } +} + +int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool disable_offloading, + u32 cmd_flags) +{ + union { + struct iwl_proto_offload_cmd_v1 v1; + struct iwl_proto_offload_cmd_v2 v2; + struct iwl_proto_offload_cmd_v3_small v3s; + struct iwl_proto_offload_cmd_v3_large v3l; + } cmd = {}; + struct iwl_host_cmd hcmd = { + .id = PROT_OFFLOAD_CONFIG_CMD, + .flags = cmd_flags, + .data[0] = &cmd, + .dataflags[0] = IWL_HCMD_DFL_DUP, + }; + struct iwl_proto_offload_cmd_common *common; + u32 enabled = 0, size; + u32 capa_flags = mvm->fw->ucode_capa.flags; +#if IS_ENABLED(CONFIG_IPV6) + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int i; + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || + capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { + struct iwl_ns_config *nsc; + struct iwl_targ_addr *addrs; + int n_nsc, n_addrs; + int c; + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { + nsc = cmd.v3s.ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; + addrs = cmd.v3s.targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; + } else { + nsc = cmd.v3l.ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; + addrs = cmd.v3l.targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; + } + + if (mvmvif->num_target_ipv6_addrs) + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + + /* + * For each address we have (and that will fit) fill a target + * address struct and combine for NS offload structs with the + * solicited node addresses. + */ + for (i = 0, c = 0; + i < mvmvif->num_target_ipv6_addrs && + i < n_addrs && c < n_nsc; i++) { + struct in6_addr solicited_addr; + int j; + + addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], + &solicited_addr); + for (j = 0; j < c; j++) + if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, + &solicited_addr) == 0) + break; + if (j == c) + c++; + addrs[i].addr = mvmvif->target_ipv6_addrs[i]; + addrs[i].config_num = cpu_to_le32(j); + nsc[j].dest_ipv6_addr = solicited_addr; + memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); + } + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) + cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); + else + cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + if (mvmvif->num_target_ipv6_addrs) { + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); + } + + BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != + sizeof(mvmvif->target_ipv6_addrs[0])); + + for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) + memcpy(cmd.v2.target_ipv6_addr[i], + &mvmvif->target_ipv6_addrs[i], + sizeof(cmd.v2.target_ipv6_addr[i])); + } else { + if (mvmvif->num_target_ipv6_addrs) { + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); + } + + BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != + sizeof(mvmvif->target_ipv6_addrs[0])); + + for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) + memcpy(cmd.v1.target_ipv6_addr[i], + &mvmvif->target_ipv6_addrs[i], + sizeof(cmd.v1.target_ipv6_addr[i])); + } +#endif + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { + common = &cmd.v3s.common; + size = sizeof(cmd.v3s); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { + common = &cmd.v3l.common; + size = sizeof(cmd.v3l); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + common = &cmd.v2.common; + size = sizeof(cmd.v2); + } else { + common = &cmd.v1.common; + size = sizeof(cmd.v1); + } + + if (vif->bss_conf.arp_addr_cnt) { + enabled |= IWL_D3_PROTO_OFFLOAD_ARP; + common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; + memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); + } + + if (!disable_offloading) + common->enabled = cpu_to_le32(enabled); + + hcmd.len[0] = size; + return iwl_mvm_send_cmd(mvm, &hcmd); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index ae347fb16a5..9545d7fdd4b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -61,6 +61,7 @@ * *****************************************************************************/ #include <linux/module.h> +#include <linux/vmalloc.h> #include <net/mac80211.h> #include "iwl-notif-wait.h" @@ -78,6 +79,7 @@ #include "iwl-prph.h" #include "rs.h" #include "fw-api-scan.h" +#include "fw-error-dump.h" #include "time-event.h" /* @@ -220,13 +222,15 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), + RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, + iwl_mvm_rx_ant_coupling_notif, true), RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), - RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), + RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true), RX_HANDLER(SCAN_OFFLOAD_COMPLETE, iwl_mvm_rx_scan_offload_complete_notif, true), RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results, @@ -321,6 +325,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(MAC_PM_POWER_TABLE), CMD(BT_COEX_CI), CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), + CMD(ANTENNA_COUPLING_NOTIFICATION), }; #undef CMD @@ -407,6 +412,10 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); + spin_lock_init(&mvm->d0i3_tx_lock); + skb_queue_head_init(&mvm->d0i3_tx); + init_waitqueue_head(&mvm->d0i3_exit_waitq); + SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); /* @@ -527,6 +536,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); + vfree(mvm->fw_error_dump); + kfree(mvm->fw_error_sram); kfree(mvm->mcast_filter_cmd); mvm->mcast_filter_cmd = NULL; @@ -690,7 +701,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); } -static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -699,9 +710,9 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) else clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - if (state && mvm->cur_ucode != IWL_UCODE_INIT) - iwl_trans_stop_device(mvm->trans); wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); + + return state && mvm->cur_ucode != IWL_UCODE_INIT; } static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) @@ -797,13 +808,52 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) } } +#ifdef CONFIG_IWLWIFI_DEBUGFS +void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + u32 file_len; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->fw_error_dump) + return; + + file_len = mvm->fw_error_sram_len + + sizeof(*dump_file) + + sizeof(*dump_data); + + dump_file = vmalloc(file_len); + if (!dump_file) + return; + + mvm->fw_error_dump = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_file->file_len = cpu_to_le32(file_len); + dump_data = (void *)dump_file->data; + dump_data->type = IWL_FW_ERROR_DUMP_SRAM; + dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); + + /* + * No need for lock since at the stage the FW isn't loaded. So it + * can't assert - we are the only one who can possibly be accessing + * mvm->fw_error_sram right now. + */ + memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len); +} +#endif + static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); iwl_mvm_dump_nic_error_log(mvm); - if (!mvm->restart_fw) - iwl_mvm_dump_sram(mvm); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_mvm_fw_error_sram_dump(mvm); +#endif iwl_mvm_nic_restart(mvm); } @@ -820,8 +870,62 @@ struct iwl_d0i3_iter_data { struct iwl_mvm *mvm; u8 ap_sta_id; u8 vif_count; + u8 offloading_tid; + bool disable_offloading; }; +static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_d0i3_iter_data *iter_data) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvmsta; + u32 available_tids = 0; + u8 tid; + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || + mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) + return false; + + ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); + if (IS_ERR_OR_NULL(ap_sta)) + return false; + + mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); + spin_lock_bh(&mvmsta->lock); + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + + /* + * in case of pending tx packets, don't use this tid + * for offloading in order to prevent reuse of the same + * qos seq counters. + */ + if (iwl_mvm_tid_queued(tid_data)) + continue; + + if (tid_data->state != IWL_AGG_OFF) + continue; + + available_tids |= BIT(tid); + } + spin_unlock_bh(&mvmsta->lock); + + /* + * disallow protocol offloading if we have no available tid + * (with no pending frames and no active aggregation, + * as we don't handle "holes" properly - the scheduler needs the + * frame's seq number and TFD index to match) + */ + if (!available_tids) + return true; + + /* for simplicity, just use the first available tid */ + iter_data->offloading_tid = ffs(available_tids) - 1; + return false; +} + static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { @@ -835,7 +939,16 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, !vif->bss_conf.assoc) return; + /* + * in case of pending tx packets or active aggregations, + * avoid offloading features in order to prevent reuse of + * the same qos seq counters. + */ + if (iwl_mvm_disallow_offloading(mvm, vif, data)) + data->disable_offloading = true; + iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); + iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); /* * on init/association, mvm already configures POWER_TABLE_CMD @@ -847,6 +960,34 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, data->vif_count++; } +static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, + struct iwl_wowlan_config_cmd_v3 *cmd, + struct iwl_d0i3_iter_data *iter_data) +{ + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvm_ap_sta; + + if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + rcu_read_lock(); + + ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]); + if (IS_ERR_OR_NULL(ap_sta)) + goto out; + + mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); + cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported; + cmd->offloading_tid = iter_data->offloading_tid; + + /* + * The d0i3 uCode takes care of the nonqos counters, + * so configure only the qos seq ones. + */ + iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common); +out: + rcu_read_unlock(); +} static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -855,11 +996,14 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) struct iwl_d0i3_iter_data d0i3_iter_data = { .mvm = mvm, }; - struct iwl_wowlan_config_cmd wowlan_config_cmd = { - .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | - IWL_WOWLAN_WAKEUP_BEACON_MISS | - IWL_WOWLAN_WAKEUP_LINK_CHANGE | - IWL_WOWLAN_WAKEUP_BCN_FILTERING), + struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = { + .common = { + .wakeup_filter = + cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | + IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE | + IWL_WOWLAN_WAKEUP_BCN_FILTERING), + }, }; struct iwl_d3_manager_config d3_cfg_cmd = { .min_sleep_time = cpu_to_le32(1000), @@ -867,17 +1011,24 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); + /* make sure we have no running tx while configuring the qos */ + set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + synchronize_net(); + ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_enter_d0i3_iterator, &d0i3_iter_data); if (d0i3_iter_data.vif_count == 1) { mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id; + mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading; } else { WARN_ON_ONCE(d0i3_iter_data.vif_count > 1); mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_offloading = false; } + iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data); ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, sizeof(wowlan_config_cmd), &wowlan_config_cmd); @@ -914,6 +1065,62 @@ static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac, ieee80211_connection_loss(vif); } +void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) +{ + struct ieee80211_sta *sta = NULL; + struct iwl_mvm_sta *mvm_ap_sta; + int i; + bool wake_queues = false; + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->d0i3_tx_lock); + + if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT) + goto out; + + IWL_DEBUG_RPM(mvm, "re-enqueue packets\n"); + + /* get the sta in order to update seq numbers and re-enqueue skbs */ + sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id], + lockdep_is_held(&mvm->mutex)); + + if (IS_ERR_OR_NULL(sta)) { + sta = NULL; + goto out; + } + + if (mvm->d0i3_offloading && qos_seq) { + /* update qos seq numbers if offloading was enabled */ + mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv; + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = le16_to_cpu(qos_seq[i]); + /* firmware stores last-used one, we store next one */ + seq += 0x10; + mvm_ap_sta->tid_data[i].seq_number = seq; + } + } +out: + /* re-enqueue (or drop) all packets */ + while (!skb_queue_empty(&mvm->d0i3_tx)) { + struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx); + + if (!sta || iwl_mvm_tx_skb(mvm, skb, sta)) + ieee80211_free_txskb(mvm->hw, skb); + + /* if the skb_queue is not empty, we need to wake queues */ + wake_queues = true; + } + clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + wake_up(&mvm->d0i3_exit_waitq); + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + if (wake_queues) + ieee80211_wake_queues(mvm->hw); + + spin_unlock_bh(&mvm->d0i3_tx_lock); +} + static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) { struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); @@ -924,6 +1131,7 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) struct iwl_wowlan_status_v6 *status; int ret; u32 disconnection_reasons, wakeup_reasons; + __le16 *qos_seq = NULL; mutex_lock(&mvm->mutex); ret = iwl_mvm_send_cmd(mvm, &get_status_cmd); @@ -935,6 +1143,7 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) status = (void *)get_status_cmd.resp_pkt->data; wakeup_reasons = le32_to_cpu(status->wakeup_reasons); + qos_seq = status->qos_seq_ctr; IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons); @@ -948,6 +1157,7 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) iwl_free_resp(&get_status_cmd); out: + iwl_mvm_d0i3_enable_tx(mvm, qos_seq); mutex_unlock(&mvm->mutex); } diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index def6ec5173b..6b636eab333 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -511,6 +511,7 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, struct iwl_power_constraint { struct ieee80211_vif *bf_vif; struct ieee80211_vif *bss_vif; + struct ieee80211_vif *p2p_vif; u16 bss_phyctx_id; u16 p2p_phyctx_id; bool pm_disabled; @@ -546,6 +547,10 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac, if (mvmvif->phy_ctxt) power_iterator->p2p_phyctx_id = mvmvif->phy_ctxt->id; + /* we should have only one P2P vif */ + WARN_ON(power_iterator->p2p_vif); + power_iterator->p2p_vif = vif; + IWL_DEBUG_POWER(mvm, "p2p: p2p_id=%d, bss_id=%d\n", power_iterator->p2p_phyctx_id, power_iterator->bss_phyctx_id); @@ -633,16 +638,18 @@ int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return ret; } - ret = iwl_mvm_power_send_cmd(mvm, vif); - if (ret) - return ret; - - if (constraint.bss_vif && vif != constraint.bss_vif) { + if (constraint.bss_vif) { ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif); if (ret) return ret; } + if (constraint.p2p_vif) { + ret = iwl_mvm_power_send_cmd(mvm, constraint.p2p_vif); + if (ret) + return ret; + } + if (!constraint.bf_vif) return 0; diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 06d8429be1f..35e86e06dff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -180,7 +180,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) .colors = { -1, -1, -1, -1 }, .new_vif = newvif, }; - u32 ll_max_duration; lockdep_assert_held(&mvm->mutex); @@ -199,21 +198,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) iwl_mvm_quota_iterator(&data, newvif->addr, newvif); } - switch (data.n_low_latency_bindings) { - case 0: /* no low latency - use default */ - ll_max_duration = 0; - break; - case 1: /* SingleBindingLowLatencyMode */ - ll_max_duration = IWL_MVM_LOWLAT_SINGLE_BINDING_MAXDUR; - break; - case 2: /* DualBindingLowLatencyMode */ - ll_max_duration = IWL_MVM_LOWLAT_DUAL_BINDING_MAXDUR; - break; - default: /* MultiBindingLowLatencyMode */ - ll_max_duration = 0; - break; - } - /* * The FW's scheduling session consists of * IWL_MVM_MAX_QUOTA fragments. Divide these fragments @@ -278,7 +262,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) * binding. */ cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN); - else cmd.quotas[idx].quota = cpu_to_le32(quota * data.n_interfaces[i]); @@ -287,11 +270,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) "Binding=%d, quota=%u > max=%u\n", idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100); - if (data.n_interfaces[i] && !data.low_latency[i]) - cmd.quotas[idx].max_duration = - cpu_to_le32(ll_max_duration); - else - cmd.quotas[idx].max_duration = cpu_to_le32(0); + cmd.quotas[idx].max_duration = cpu_to_le32(0); idx++; } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 399709f2be2..568abd61b14 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -211,9 +211,9 @@ static const struct rs_tx_column rs_tx_columns[] = { .next_columns = { RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_SISO_ANT_A, + RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, + RS_COLUMN_MIMO2_SGI, }, }, [RS_COLUMN_LEGACY_ANT_B] = { @@ -221,10 +221,10 @@ static const struct rs_tx_column rs_tx_columns[] = { .ant = ANT_B, .next_columns = { RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_SISO_ANT_A, RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, + RS_COLUMN_MIMO2_SGI, }, }, [RS_COLUMN_SISO_ANT_A] = { @@ -234,8 +234,8 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2, RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_MIMO2_SGI, }, .checks = { rs_siso_allow, @@ -248,8 +248,8 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_A, RS_COLUMN_MIMO2, RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2_SGI, }, .checks = { rs_siso_allow, @@ -263,8 +263,8 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_MIMO2_SGI, RS_COLUMN_SISO_ANT_A, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, }, .checks = { rs_siso_allow, @@ -279,8 +279,8 @@ static const struct rs_tx_column rs_tx_columns[] = { RS_COLUMN_SISO_ANT_A_SGI, RS_COLUMN_MIMO2_SGI, RS_COLUMN_SISO_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, }, .checks = { rs_siso_allow, @@ -292,10 +292,10 @@ static const struct rs_tx_column rs_tx_columns[] = { .ant = ANT_AB, .next_columns = { RS_COLUMN_SISO_ANT_A, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_MIMO2_SGI, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, }, .checks = { rs_mimo_allow, @@ -307,10 +307,10 @@ static const struct rs_tx_column rs_tx_columns[] = { .sgi = true, .next_columns = { RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, }, .checks = { rs_mimo_allow, @@ -503,6 +503,14 @@ static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) window->average_tpt = IWL_INVALID_VALUE; } +static void rs_rate_scale_clear_tbl_windows(struct iwl_scale_tbl_info *tbl) +{ + int i; + + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&tbl->win[i]); +} + static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) { return (ant_type & valid_antenna) == ant_type; @@ -566,19 +574,13 @@ static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) * at this rate. window->data contains the bitmask of successful * packets. */ -static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes) +static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes, + struct iwl_rate_scale_data *window) { - struct iwl_rate_scale_data *window = NULL; static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); s32 fail_count, tpt; - if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) - return -EINVAL; - - /* Select window for current tx bit rate */ - window = &(tbl->win[scale_index]); - /* Get expected throughput */ tpt = get_expected_tpt(tbl, scale_index); @@ -636,6 +638,21 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, return 0; } +static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes) +{ + struct iwl_rate_scale_data *window = NULL; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + /* Select window for current tx bit rate */ + window = &(tbl->win[scale_index]); + + return _rs_collect_tx_data(tbl, scale_index, attempts, successes, + window); +} + /* Convert rs_rate object into ucode rate bitmask */ static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, struct rs_rate *rate) @@ -1361,7 +1378,6 @@ static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) { struct iwl_scale_tbl_info *tbl; - int i; int active_tbl; int flush_interval_passed = 0; struct iwl_mvm *mvm; @@ -1422,9 +1438,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) IWL_DEBUG_RATE(mvm, "LQ: stay in table clear win\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window( - &(tbl->win[i])); + rs_rate_scale_clear_tbl_windows(tbl); } } @@ -1433,8 +1447,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) * "search" table). */ if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { IWL_DEBUG_RATE(mvm, "Clearing up window stats\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); + rs_rate_scale_clear_tbl_windows(tbl); } } } @@ -1724,7 +1737,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, int low = IWL_RATE_INVALID; int high = IWL_RATE_INVALID; int index; - int i; struct iwl_rate_scale_data *window = NULL; int current_tpt = IWL_INVALID_VALUE; int low_tpt = IWL_INVALID_VALUE; @@ -2009,8 +2021,7 @@ lq_update: if (lq_sta->search_better_tbl) { /* Access the "search" table, clear its history. */ tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); + rs_rate_scale_clear_tbl_windows(tbl); /* Use new "search" start rate */ index = tbl->rate.index; @@ -2331,8 +2342,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->lq.sta_id = sta_priv->sta_id; for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); + rs_rate_scale_clear_tbl_windows(&lq_sta->lq_info[j]); lq_sta->flush_timer = 0; @@ -2591,7 +2601,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, if (sta) lq_cmd->agg_time_limit = - cpu_to_le16(iwl_mvm_bt_coex_agg_time_limit(mvm, sta)); + cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 713efd71efe..c91dc849885 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -70,9 +70,16 @@ #define IWL_PLCP_QUIET_THRESH 1 #define IWL_ACTIVE_QUIET_TIME 10 -#define LONG_OUT_TIME_PERIOD 600 -#define SHORT_OUT_TIME_PERIOD 200 -#define SUSPEND_TIME_PERIOD 100 + +struct iwl_mvm_scan_params { + u32 max_out_time; + u32 suspend_time; + bool passive_fragmented; + struct _dwell { + u16 passive; + u16 active; + } dwell[IEEE80211_NUM_BANDS]; +}; static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) { @@ -90,24 +97,6 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) return cpu_to_le16(rx_chain); } -static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif, - u32 flags, bool is_assoc) -{ - if (!is_assoc) - return 0; - if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY) - return cpu_to_le32(ieee80211_tu_to_usec(SHORT_OUT_TIME_PERIOD)); - return cpu_to_le32(ieee80211_tu_to_usec(LONG_OUT_TIME_PERIOD)); -} - -static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif, - bool is_assoc) -{ - if (!is_assoc) - return 0; - return cpu_to_le32(ieee80211_tu_to_usec(SUSPEND_TIME_PERIOD)); -} - static inline __le32 iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req) { @@ -181,15 +170,14 @@ static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band) static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, struct cfg80211_scan_request *req, - bool basic_ssid) + bool basic_ssid, + struct iwl_mvm_scan_params *params) { - u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band); - u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band, - req->n_ssids); struct iwl_scan_channel *chan = (struct iwl_scan_channel *) (cmd->data + le16_to_cpu(cmd->tx_cmd.len)); int i; int type = BIT(req->n_ssids) - 1; + enum ieee80211_band band = req->channels[0]->band; if (!basic_ssid) type |= BIT(req->n_ssids); @@ -199,8 +187,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, chan->type = cpu_to_le32(type); if (req->channels[i]->flags & IEEE80211_CHAN_NO_IR) chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE); - chan->active_dwell = cpu_to_le16(active_dwell); - chan->passive_dwell = cpu_to_le16(passive_dwell); + chan->active_dwell = cpu_to_le16(params->dwell[band].active); + chan->passive_dwell = cpu_to_le16(params->dwell[band].passive); chan->iteration_count = cpu_to_le16(1); chan++; } @@ -267,13 +255,76 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, return (u16)len; } -static void iwl_mvm_vif_assoc_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) +static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) { - bool *is_assoc = data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *global_bound = data; - if (vif->bss_conf.assoc) - *is_assoc = true; + if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS) + *global_bound = true; +} + +static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + int n_ssids, + struct iwl_mvm_scan_params *params) +{ + bool global_bound = false; + enum ieee80211_band band; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_scan_condition_iterator, + &global_bound); + /* + * Under low latency traffic passive scan is fragmented meaning + * that dwell on a particular channel will be fragmented. Each fragment + * dwell time is 20ms and fragments period is 105ms. Skipping to next + * channel will be delayed by the same period - 105ms. So suspend_time + * parameter describing both fragments and channels skipping periods is + * set to 105ms. This value is chosen so that overall passive scan + * duration will not be too long. Max_out_time in this case is set to + * 70ms, so for active scanning operating channel will be left for 70ms + * while for passive still for 20ms (fragment dwell). + */ + if (global_bound) { + if (!iwl_mvm_low_latency(mvm)) { + params->suspend_time = ieee80211_tu_to_usec(100); + params->max_out_time = ieee80211_tu_to_usec(600); + } else { + params->suspend_time = ieee80211_tu_to_usec(105); + /* P2P doesn't support fragmented passive scan, so + * configure max_out_time to be at least longest dwell + * time for passive scan. + */ + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { + params->max_out_time = ieee80211_tu_to_usec(70); + params->passive_fragmented = true; + } else { + u32 passive_dwell; + + /* + * Use band G so that passive channel dwell time + * will be assigned with maximum value. + */ + band = IEEE80211_BAND_2GHZ; + passive_dwell = iwl_mvm_get_passive_dwell(band); + params->max_out_time = + ieee80211_tu_to_usec(passive_dwell); + } + } + } + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + if (params->passive_fragmented) + params->dwell[band].passive = 20; + else + params->dwell[band].passive = + iwl_mvm_get_passive_dwell(band); + params->dwell[band].active = iwl_mvm_get_active_dwell(band, + n_ssids); + } } int iwl_mvm_scan_request(struct iwl_mvm *mvm, @@ -288,13 +339,13 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, }; struct iwl_scan_cmd *cmd = mvm->scan_cmd; - bool is_assoc = false; int ret; u32 status; int ssid_len = 0; u8 *ssid = NULL; bool basic_ssid = !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID); + struct iwl_mvm_scan_params params = {}; lockdep_assert_held(&mvm->mutex); BUG_ON(mvm->scan_cmd == NULL); @@ -304,17 +355,18 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, memset(cmd, 0, sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel))); - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_vif_assoc_iterator, - &is_assoc); + cmd->channel_count = (u8)req->n_channels; cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm); - cmd->max_out_time = iwl_mvm_scan_max_out_time(vif, req->flags, - is_assoc); - cmd->suspend_time = iwl_mvm_scan_suspend_time(vif, is_assoc); + + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + cmd->max_out_time = cpu_to_le32(params.max_out_time); + cmd->suspend_time = cpu_to_le32(params.suspend_time); + if (params.passive_fragmented) + cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN; + cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); @@ -360,7 +412,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, req->ie, req->ie_len, mvm->fw->ucode_capa.max_probe_length)); - iwl_mvm_scan_fill_channels(cmd, req, basic_ssid); + iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, ¶ms); cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) + le16_to_cpu(cmd->tx_cmd.len) + @@ -402,10 +454,13 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scan_complete_notif *notif = (void *)pkt->data; + lockdep_assert_held(&mvm->mutex); + IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n", notif->status, notif->scanned_channels); - mvm->scan_status = IWL_MVM_SCAN_NONE; + if (mvm->scan_status == IWL_MVM_SCAN_OS) + mvm->scan_status = IWL_MVM_SCAN_NONE; ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); @@ -466,7 +521,7 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait, }; } -void iwl_mvm_cancel_scan(struct iwl_mvm *mvm) +int iwl_mvm_cancel_scan(struct iwl_mvm *mvm) { struct iwl_notification_wait wait_scan_abort; static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD, @@ -474,13 +529,13 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm) int ret; if (mvm->scan_status == IWL_MVM_SCAN_NONE) - return; + return 0; if (iwl_mvm_is_radio_killed(mvm)) { ieee80211_scan_completed(mvm->hw, true); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); mvm->scan_status = IWL_MVM_SCAN_NONE; - return; + return 0; } iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort, @@ -495,14 +550,11 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm) goto out_remove_notif; } - ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, 1 * HZ); - if (ret) - IWL_ERR(mvm, "%s - failed on timeout\n", __func__); - - return; + return iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, HZ); out_remove_notif: iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort); + return ret; } int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, @@ -519,10 +571,11 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? "completed" : "aborted"); - /* might already be something else again, don't reset if so */ - if (mvm->scan_status == IWL_MVM_SCAN_SCHED) + /* only call mac80211 completion if the stop was initiated by FW */ + if (mvm->scan_status == IWL_MVM_SCAN_SCHED) { mvm->scan_status = IWL_MVM_SCAN_NONE; - ieee80211_sched_scan_stopped(mvm->hw); + ieee80211_sched_scan_stopped(mvm->hw); + } return 0; } @@ -553,14 +606,9 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, static void iwl_build_scan_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct iwl_scan_offload_cmd *scan) + struct iwl_scan_offload_cmd *scan, + struct iwl_mvm_scan_params *params) { - bool is_assoc = false; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_vif_assoc_iterator, - &is_assoc); scan->channel_count = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; @@ -568,13 +616,17 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm, scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT; scan->rx_chain = iwl_mvm_scan_rx_chain(mvm); - scan->max_out_time = iwl_mvm_scan_max_out_time(vif, req->flags, - is_assoc); - scan->suspend_time = iwl_mvm_scan_suspend_time(vif, is_assoc); + + scan->max_out_time = cpu_to_le32(params->max_out_time); + scan->suspend_time = cpu_to_le32(params->suspend_time); + scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND); scan->rep_count = cpu_to_le32(1); + + if (params->passive_fragmented) + scan->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN; } static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) @@ -639,12 +691,11 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, struct iwl_scan_channel_cfg *channels, enum ieee80211_band band, int *head, int *tail, - u32 ssid_bitmap) + u32 ssid_bitmap, + struct iwl_mvm_scan_params *params) { struct ieee80211_supported_band *s_band; - int n_probes = req->n_ssids; int n_channels = req->n_channels; - u8 active_dwell, passive_dwell; int i, j, index = 0; bool partial; @@ -654,8 +705,6 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, * to scan. So add requested channels to head of the list and others to * the end. */ - active_dwell = iwl_mvm_get_active_dwell(band, n_probes); - passive_dwell = iwl_mvm_get_passive_dwell(band); s_band = &mvm->nvm_data->bands[band]; for (i = 0; i < s_band->n_channels && *head <= *tail; i++) { @@ -679,8 +728,8 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, channels->channel_number[index] = cpu_to_le16(ieee80211_frequency_to_channel( s_band->channels[i].center_freq)); - channels->dwell_time[index][0] = active_dwell; - channels->dwell_time[index][1] = passive_dwell; + channels->dwell_time[index][0] = params->dwell[band].active; + channels->dwell_time[index][1] = params->dwell[band].passive; channels->iter_count[index] = cpu_to_le16(1); channels->iter_interval[index] = 0; @@ -709,7 +758,6 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies) { - int supported_bands = 0; int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; int head = 0; @@ -723,22 +771,19 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, .id = SCAN_OFFLOAD_CONFIG_CMD, .flags = CMD_SYNC, }; + struct iwl_mvm_scan_params params = {}; lockdep_assert_held(&mvm->mutex); - if (band_2ghz) - supported_bands++; - if (band_5ghz) - supported_bands++; - cmd_len = sizeof(struct iwl_scan_offload_cfg) + - supported_bands * SCAN_OFFLOAD_PROBE_REQ_SIZE; + 2 * SCAN_OFFLOAD_PROBE_REQ_SIZE; scan_cfg = kzalloc(cmd_len, GFP_KERNEL); if (!scan_cfg) return -ENOMEM; - iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd); + iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, ¶ms); + iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, ¶ms); scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap); @@ -750,7 +795,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, scan_cfg->data); iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, IEEE80211_BAND_2GHZ, &head, &tail, - ssid_bitmap); + ssid_bitmap, ¶ms); } if (band_5ghz) { iwl_scan_offload_build_tx_cmd(mvm, vif, ies, @@ -760,7 +805,7 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, SCAN_OFFLOAD_PROBE_REQ_SIZE); iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, IEEE80211_BAND_5GHZ, &head, &tail, - ssid_bitmap); + ssid_bitmap, ¶ms); } cmd.data[0] = scan_cfg; @@ -900,26 +945,49 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) * microcode has notified us that a scan is completed. */ IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); - ret = -EIO; + ret = -ENOENT; } return ret; } -void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm) +int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm) { int ret; + struct iwl_notification_wait wait_scan_done; + static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, }; lockdep_assert_held(&mvm->mutex); if (mvm->scan_status != IWL_MVM_SCAN_SCHED) { IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n"); - return; + return 0; } + iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done, + scan_done_notif, + ARRAY_SIZE(scan_done_notif), + NULL, NULL); + ret = iwl_mvm_send_sched_scan_abort(mvm); - if (ret) + if (ret) { IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret); - else - IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n"); + iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); + return ret; + } + + IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n"); + + ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); + if (ret) + return ret; + + /* + * Clear the scan status so the next scan requests will succeed. This + * also ensures the Rx handler doesn't do anything, as the scan was + * stopped from above. + */ + mvm->scan_status = IWL_MVM_SCAN_NONE; + + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 2677d1c0e1a..f339ef88425 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -851,7 +851,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, return ret; } -static const u8 tid_to_mac80211_ac[] = { +const u8 tid_to_mac80211_ac[] = { IEEE80211_AC_BE, IEEE80211_AC_BK, IEEE80211_AC_BK, @@ -902,10 +902,18 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return -EIO; } + spin_lock_bh(&mvmsta->lock); + + /* possible race condition - we entered D0i3 while starting agg */ + if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) { + spin_unlock_bh(&mvmsta->lock); + IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n"); + return -EIO; + } + /* the new tx queue is still connected to the same mac80211 queue */ mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]]; - spin_lock_bh(&mvmsta->lock); tid_data = &mvmsta->tid_data[tid]; tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); tid_data->txq_id = txq_id; diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 0ba96654d2c..879aeac46cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -79,6 +79,7 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, __le16 fc = hdr->frame_control; u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags); u32 len = skb->len + FCS_LEN; + u8 ac; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) tx_flags |= TX_CMD_FLG_ACK; @@ -90,13 +91,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; - /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->band == IEEE80211_BAND_2GHZ && - (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_back_req(fc) || ieee80211_is_mgmt(fc))) - tx_flags |= TX_CMD_FLG_BT_DIS; - if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG; @@ -112,6 +106,11 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_flags &= ~TX_CMD_FLG_SEQ_CTL; } + /* tid_tspec will default to 0 = BE when QOS isn't enabled */ + ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; + tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << + TX_CMD_FLG_BT_PRIO_POS; + if (ieee80211_is_mgmt(fc)) { if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->pm_frame_timeout = cpu_to_le16(3); @@ -128,9 +127,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_cmd->pm_frame_timeout = 0; } - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_flags |= TX_CMD_FLG_PROT_REQUIRE; - if (ieee80211_is_data(fc) && len > mvm->rts_threshold && !is_multicast_ether_addr(ieee80211_get_DA(hdr))) tx_flags |= TX_CMD_FLG_PROT_REQUIRE; diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index bbfe529d7b6..d619851745a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -516,33 +516,26 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) iwl_mvm_dump_umac_error_log(mvm); } -void iwl_mvm_dump_sram(struct iwl_mvm *mvm) +void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm) { const struct fw_img *img; - int ofs, len = 0; - int i; - __le32 *buf; + u32 ofs, sram_len; + void *sram; - if (!mvm->ucode_loaded) + if (!mvm->ucode_loaded || mvm->fw_error_sram) return; img = &mvm->fw->img[mvm->cur_ucode]; ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - len = img->sec[IWL_UCODE_SECTION_DATA].len; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - buf = kzalloc(len, GFP_ATOMIC); - if (!buf) + sram = kzalloc(sram_len, GFP_ATOMIC); + if (!sram) return; - iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len); - len = len >> 2; - for (i = 0; i < len; i++) { - IWL_ERR(mvm, "0x%08X\n", le32_to_cpu(buf[i])); - /* Add a small delay to let syslog catch up */ - udelay(10); - } - - kfree(buf); + iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len); + mvm->fw_error_sram = sram; + mvm->fw_error_sram_len = sram_len; } /** @@ -619,6 +612,9 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); + if (mvmvif->low_latency == value) + return 0; + mvmvif->low_latency = value; res = iwl_mvm_update_quotas(mvm, NULL); @@ -629,3 +625,22 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return iwl_mvm_power_update_mac(mvm, vif); } + +static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *result = _data; + + if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif))) + *result = true; +} + +bool iwl_mvm_low_latency(struct iwl_mvm *mvm) +{ + bool result = false; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_ll_iter, &result); + + return result; +} diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 1f97631a82e..edb015c9904 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -447,7 +447,8 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) pxsx_handle = ACPI_HANDLE(&pdev->dev); if (!pxsx_handle) { - IWL_ERR(trans, "Could not retrieve root port ACPI handle"); + IWL_DEBUG_INFO(trans, + "Could not retrieve root port ACPI handle"); return; } @@ -559,7 +560,7 @@ static int iwl_pci_resume(struct device *device) iwl_enable_rfkill_int(trans); hw_rfkill = iwl_is_rfkill_set(trans); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); return 0; } diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 3120bc5bb12..9091513ea73 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -488,4 +488,6 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask); } +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index cf49f6ce0ff..fdfa3969cac 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -994,7 +994,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) isr_stats->rfkill++; - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); if (hw_rfkill) { set_bit(STATUS_RFKILL, &trans->status); if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 84d471299e5..dcfd6d866d0 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -75,6 +75,20 @@ #include "iwl-agn-hw.h" #include "internal.h" +static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) +{ + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (2 << 28))); + return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); +} + +static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) +{ + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val); + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (3 << 28))); +} + static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) { if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) @@ -229,6 +243,116 @@ out: return ret; } +/* + * Enable LP XTAL to avoid HW bug where device may consume much power if + * FW is not loaded after device reset. LP XTAL is disabled by default + * after device HW reset. Do it only if XTAL is fed by internal source. + * Configure device's "persistence" mode to avoid resetting XTAL again when + * SHRD_HW_RST occurs in S3. + */ +static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) +{ + int ret; + u32 apmg_gp1_reg; + u32 apmg_xtal_cfg_reg; + u32 dl_cfg_reg; + + /* Force XTAL ON */ + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + + /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is possible. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (WARN_ON(ret < 0)) { + IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n"); + /* Release XTAL ON request */ + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + return; + } + + /* + * Clear "disable persistence" to avoid LP XTAL resetting when + * SHRD_HW_RST is applied in S3. + */ + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_PERSIST_DIS); + + /* + * Force APMG XTAL to be active to prevent its disabling by HW + * caused by APMG idle state. + */ + apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans, + SHR_APMG_XTAL_CFG_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, + apmg_xtal_cfg_reg | + SHR_APMG_XTAL_CFG_XTAL_ON_REQ); + + /* + * Reset entire device again - do controller reset (results in + * SHRD_HW_RST). Turn MAC off before proceeding. + */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* Enable LP XTAL by indirect access through CSR */ + apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg | + SHR_APMG_GP1_WF_XTAL_LP_EN | + SHR_APMG_GP1_CHICKEN_BIT_SELECT); + + /* Clear delay line clock power up */ + dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg & + ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP); + + /* + * Enable persistence mode to avoid LP XTAL resetting when + * SHRD_HW_RST is applied in S3. + */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PERSIST_MODE); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* Activates XTAL resources monitor */ + __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, + CSR_MONITOR_XTAL_RESOURCES); + + /* Release XTAL ON request */ + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + udelay(10); + + /* Release APMG XTAL */ + iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, + apmg_xtal_cfg_reg & + ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); +} + static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) { int ret = 0; @@ -256,6 +380,11 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans) /* Stop device's DMA activity */ iwl_pcie_apm_stop_master(trans); + if (trans->cfg->lp_xtal_workaround) { + iwl_pcie_apm_lp_xtal_enable(trans); + return; + } + /* Reset the entire device */ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); @@ -641,7 +770,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, set_bit(STATUS_RFKILL, &trans->status); else clear_bit(STATUS_RFKILL, &trans->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); if (hw_rfkill && !run_in_rfkill) return -ERFKILL; @@ -756,7 +885,13 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) else clear_bit(STATUS_RFKILL, &trans->status); if (hw_rfkill != was_hw_rfkill) - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); +} + +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) +{ + if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) + iwl_trans_pcie_stop_device(trans); } static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) @@ -865,7 +1000,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) set_bit(STATUS_RFKILL, &trans->status); else clear_bit(STATUS_RFKILL, &trans->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); return 0; } @@ -1208,6 +1343,7 @@ static const char *get_csr_string(int cmd) IWL_CMD(CSR_GIO_CHICKEN_BITS); IWL_CMD(CSR_ANA_PLL_CFG); IWL_CMD(CSR_HW_REV_WA_REG); + IWL_CMD(CSR_MONITOR_STATUS_REG); IWL_CMD(CSR_DBG_HPET_MEM_REG); default: return "UNKNOWN"; @@ -1240,6 +1376,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans) CSR_DRAM_INT_TBL_REG, CSR_GIO_CHICKEN_BITS, CSR_ANA_PLL_CFG, + CSR_MONITOR_STATUS_REG, CSR_HW_REV_WA_REG, CSR_DBG_HPET_MEM_REG }; diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index d9c65b60cd7..d14ead8beca 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -159,28 +159,34 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, int tid; struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; + u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) & SSN_MASK); - tid = (le16_to_cpu(add_ba_rsp->block_ack_param_set) - & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { - tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, - add_ba_rsp->peer_mac_addr); - if (tx_ba_tbl) { - dev_dbg(priv->adapter->dev, "info: BA stream complete\n"); - tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; - } else { - dev_err(priv->adapter->dev, "BA stream not created\n"); - } - } else { + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, TYPE_DELBA_SENT, true); if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) priv->aggr_prio_tbl[tid].ampdu_ap = BA_STREAM_NOT_ALLOWED; + return 0; + } + + tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); + if (tx_ba_tbl) { + dev_dbg(priv->adapter->dev, "info: BA stream complete\n"); + tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tx_ba_tbl->amsdu = true; + else + tx_ba_tbl->amsdu = false; + } else { + dev_err(priv->adapter->dev, "BA stream not created\n"); } return 0; @@ -540,6 +546,7 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) u32 tx_win_size = priv->add_ba_param.tx_win_size; static u8 dialog_tok; int ret; + u16 block_ack_param_set; dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid); @@ -558,10 +565,16 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; } - add_ba_req.block_ack_param_set = cpu_to_le16( - (u16) ((tid << BLOCKACKPARAM_TID_POS) | - tx_win_size << BLOCKACKPARAM_WINSIZE_POS | - IMMEDIATE_BLOCK_ACK)); + block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | + tx_win_size << BLOCKACKPARAM_WINSIZE_POS | + IMMEDIATE_BLOCK_ACK); + + /* enable AMSDU inside AMPDU */ + if (priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; + + add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set); add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); ++dialog_tok; @@ -676,6 +689,7 @@ int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, dev_dbg(priv->adapter->dev, "data: %s tid=%d\n", __func__, rx_reo_tbl->tid); memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); + rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu; rx_reo_tbl++; count++; if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) @@ -731,5 +745,8 @@ void mwifiex_set_ba_params(struct mwifiex_private *priv) MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; } + priv->add_ba_param.tx_amsdu = true; + priv->add_ba_param.rx_amsdu = true; + return; } diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 12bb6acbdd5..40b007a00f4 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -76,6 +76,20 @@ mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; } +/* This function checks whether AMSDU is allowed for BA stream. */ +static inline u8 +mwifiex_is_amsdu_in_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + struct mwifiex_tx_ba_stream_tbl *tx_tbl; + + tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra); + if (tx_tbl) + return tx_tbl->amsdu; + + return false; +} + /* This function checks whether AMPDU is allowed or not for a particular TID. */ static inline u8 mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index c3323c49261..0c3571f830b 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -26,6 +26,56 @@ #include "11n.h" #include "11n_rxreorder.h" +/* This function will dispatch amsdu packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); + int ret; + + if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { + struct sk_buff_head list; + struct sk_buff *rx_skb; + + __skb_queue_head_init(&list); + + skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); + skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); + + ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, + priv->wdev->iftype, 0, false); + + while (!skb_queue_empty(&list)) { + rx_skb = __skb_dequeue(&list); + ret = mwifiex_recv_packet(priv, rx_skb); + if (ret == -1) + dev_err(priv->adapter->dev, + "Rx of A-MSDU failed"); + } + return 0; + } + + return -1; +} + +/* This function will process the rx packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) +{ + int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); + + if (!ret) + return 0; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_handle_uap_rx_forward(priv, payload); + + return mwifiex_process_rx_packet(priv, payload); +} + /* * This function dispatches all packets in the Rx reorder table until the * start window. @@ -35,8 +85,9 @@ * circular buffer. */ static void -mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl, int start_win) +mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl, + int start_win) { int pkt_to_send, i; void *rx_tmp_ptr; @@ -54,12 +105,8 @@ mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, tbl->rx_reorder_ptr[i] = NULL; } spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - if (rx_tmp_ptr) { - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr); - else - mwifiex_process_rx_packet(priv, rx_tmp_ptr); - } + if (rx_tmp_ptr) + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); } spin_lock_irqsave(&priv->rx_pkt_lock, flags); @@ -101,11 +148,7 @@ mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, rx_tmp_ptr = tbl->rx_reorder_ptr[i]; tbl->rx_reorder_ptr[i] = NULL; spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - mwifiex_handle_uap_rx_forward(priv, rx_tmp_ptr); - else - mwifiex_process_rx_packet(priv, rx_tmp_ptr); + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); } spin_lock_irqsave(&priv->rx_pkt_lock, flags); @@ -135,12 +178,13 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl) { unsigned long flags; + int start_win; if (!tbl) return; - mwifiex_11n_dispatch_pkt(priv, tbl, (tbl->start_win + tbl->win_size) & - (MAX_TID_VALUE - 1)); + start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); del_timer_sync(&tbl->timer_context.timer); @@ -228,17 +272,17 @@ mwifiex_flush_data(unsigned long context) { struct reorder_tmr_cnxt *ctx = (struct reorder_tmr_cnxt *) context; - int start_win; + int start_win, seq_num; - start_win = mwifiex_11n_find_last_seq_num(ctx->ptr); + seq_num = mwifiex_11n_find_last_seq_num(ctx->ptr); - if (start_win < 0) + if (seq_num < 0) return; - dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", start_win); - mwifiex_11n_dispatch_pkt(ctx->priv, ctx->ptr, - (ctx->ptr->start_win + start_win + 1) & - (MAX_TID_VALUE - 1)); + dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", seq_num); + start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, + start_win); } /* @@ -267,7 +311,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, */ tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (tbl) { - mwifiex_11n_dispatch_pkt(priv, tbl, seq_num); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); return; } /* if !tbl then create one */ @@ -401,8 +445,11 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, >> BLOCKACKPARAM_TID_POS; add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; - /* We donot support AMSDU inside AMPDU, hence reset the bit */ - block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + + /* If we don't support AMSDU inside AMPDU, reset the bit */ + if (!priv->add_ba_param.rx_amsdu || + (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) + block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) @@ -459,14 +506,16 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (!tbl) { - if (pkt_type != PKT_TYPE_BAR) { - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - mwifiex_handle_uap_rx_forward(priv, payload); - else - mwifiex_process_rx_packet(priv, payload); - } + if (pkt_type != PKT_TYPE_BAR) + mwifiex_11n_dispatch_pkt(priv, payload); return 0; } + + if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { + mwifiex_11n_dispatch_pkt(priv, payload); + return 0; + } + start_win = tbl->start_win; win_size = tbl->win_size; end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); @@ -520,7 +569,7 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, start_win = (end_win - win_size) + 1; else start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1; - mwifiex_11n_dispatch_pkt(priv, tbl, start_win); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); } if (pkt_type != PKT_TYPE_BAR) { @@ -611,16 +660,7 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, * Check if we had rejected the ADDBA, if yes then do not create * the stream */ - if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) { - win_size = (block_ack_param_set & - IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) - >> BLOCKACKPARAM_WINSIZE_POS; - - dev_dbg(priv->adapter->dev, - "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", - add_ba_rsp->peer_mac_addr, tid, - add_ba_rsp->ssn, win_size); - } else { + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n", add_ba_rsp->peer_mac_addr, tid); @@ -628,8 +668,28 @@ int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, add_ba_rsp->peer_mac_addr); if (tbl) mwifiex_del_rx_reorder_entry(priv, tbl); + + return 0; } + win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) { + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tbl->amsdu = true; + else + tbl->amsdu = false; + } + + dev_dbg(priv->adapter->dev, + "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", + add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); + return 0; } diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 51ce99cfcfb..21ee27ab7b7 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1158,9 +1158,10 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; enum ieee80211_band band; + struct mwifiex_adapter *adapter = priv->adapter; if (!priv->media_connected) { - dev_err(priv->adapter->dev, + dev_err(adapter->dev, "Can not set Tx data rate in disconnected state\n"); return -EINVAL; } @@ -1181,9 +1182,16 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, /* Fill HT MCS rates */ bitmap_rates[2] = mask->control[band].ht_mcs[0]; - if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; + /* Fill VHT MCS rates */ + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + bitmap_rates[10] = mask->control[band].vht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[11] = mask->control[band].vht_mcs[1]; + } + return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); } @@ -2092,10 +2100,10 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, else ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; - if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + if (adapter->user_dev_mcs_support == HT_STREAM_2X2) + ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT; else - ht_info->cap &= ~(3 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 14e05c9f466..b4115582922 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1502,6 +1502,7 @@ 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)) { diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 39cb3542f79..b485dc1ae5e 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -202,6 +202,11 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR +#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC) +#define MWIFIEX_RX_STBC1 0x0100 +#define MWIFIEX_RX_STBC12 0x0200 +#define MWIFIEX_RX_STBC123 0x0300 + /* dev_cap bitmap * BIT * 0-16 reserved @@ -515,6 +520,8 @@ enum P2P_MODES { #define ACT_TDLS_CREATE 0x01 #define ACT_TDLS_CONFIG 0x02 +#define MWIFIEX_FW_V15 15 + struct mwifiex_ie_types_header { __le16 type; __le16 len; @@ -1103,6 +1110,7 @@ struct mwifiex_rate_scope { __le16 hr_dsss_rate_bitmap; __le16 ofdm_rate_bitmap; __le16 ht_mcs_rate_bitmap[8]; + __le16 vht_mcs_rate_bitmap[8]; } __packed; struct mwifiex_rate_drop_pattern { diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 1fb2212079a..ee494db5406 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -177,6 +177,7 @@ struct mwifiex_ds_rx_reorder_tbl { struct mwifiex_ds_tx_ba_stream_tbl { u16 tid; u8 ra[ETH_ALEN]; + u8 amsdu; }; #define DBG_CMD_NUM 5 diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index f0289c12e04..a67f7da12b3 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -116,7 +116,7 @@ enum { #define MWIFIEX_TYPE_DATA 0 #define MWIFIEX_TYPE_EVENT 3 -#define MAX_BITMAP_RATES_SIZE 10 +#define MAX_BITMAP_RATES_SIZE 18 #define MAX_CHANNEL_BAND_BG 14 #define MAX_CHANNEL_BAND_A 165 @@ -192,6 +192,8 @@ struct mwifiex_add_ba_param { u32 tx_win_size; u32 rx_win_size; u32 timeout; + u8 tx_amsdu; + u8 rx_amsdu; }; struct mwifiex_tx_aggr { @@ -560,6 +562,7 @@ struct mwifiex_tx_ba_stream_tbl { int tid; u8 ra[ETH_ALEN]; enum mwifiex_ba_status ba_status; + u8 amsdu; }; struct mwifiex_rx_reorder_tbl; @@ -579,6 +582,7 @@ struct mwifiex_rx_reorder_tbl { int win_size; void **rx_reorder_ptr; struct reorder_tmr_cnxt timer_context; + u8 amsdu; u8 flags; }; @@ -802,6 +806,7 @@ struct mwifiex_adapter { atomic_t pending_bridged_pkts; struct semaphore *card_sem; bool ext_scan; + u8 fw_api_ver; u8 fw_key_api_major_ver, fw_key_api_minor_ver; }; diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 9f1683b5f28..57c353a94b2 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -327,6 +327,30 @@ static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter) return; } +static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, + u32 max_delay_loop_cnt) +{ + struct pcie_service_card *card = adapter->card; + u8 *buffer; + u32 sleep_cookie, count; + + for (count = 0; count < max_delay_loop_cnt; count++) { + buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN; + sleep_cookie = *(u32 *)buffer; + + if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { + dev_dbg(adapter->dev, + "sleep cookie found at count %d\n", count); + break; + } + usleep_range(20, 30); + } + + if (count >= max_delay_loop_cnt) + dev_dbg(adapter->dev, + "max count reached while accessing sleep cookie\n"); +} + /* This function wakes up the card by reading fw_status register. */ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { @@ -1539,6 +1563,8 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) "Write register failed\n"); return -1; } + mwifiex_delay_for_sleep_cookie(adapter, + MWIFIEX_MAX_DELAY_COUNT); while (reg->sleep_cookie && (count++ < 10) && mwifiex_pcie_ok_to_access_hw(adapter)) usleep_range(50, 60); diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index 193af75bf58..e8ec561f8a6 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -97,6 +97,8 @@ #define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256 /* FW awake cookie after FW ready */ #define FW_AWAKE_COOKIE (0xAA55AA55) +#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF +#define MWIFIEX_MAX_DELAY_COUNT 5 struct mwifiex_pcie_card_reg { u16 cmd_addr_lo; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 4315a3ba3b9..e3cac1495cc 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -185,6 +185,13 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[10 + i]); + } } else { rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(priv->bitmap_rates[0]); @@ -195,6 +202,13 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, i++) rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(priv->bitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[10 + i]); + } } rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index a8f7d545e22..bfebb0144df 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -304,6 +304,15 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, priv->bitmap_rates[2 + i] = le16_to_cpu(rate_scope-> ht_mcs_rate_bitmap[i]); + + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; i < ARRAY_SIZE(rate_scope-> + vht_mcs_rate_bitmap); + i++) + priv->bitmap_rates[10 + i] = + le16_to_cpu(rate_scope-> + vht_mcs_rate_bitmap[i]); + } break; /* Add RATE_DROP tlv here */ } diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index b6aa958bd6e..ed26387eccf 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -201,26 +201,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, return ret; } - if (rx_pkt_type == PKT_TYPE_AMSDU) { - struct sk_buff_head list; - struct sk_buff *rx_skb; - - __skb_queue_head_init(&list); - - skb_pull(skb, rx_pkt_offset); - skb_trim(skb, rx_pkt_length); - - ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, - priv->wdev->iftype, 0, false); - - while (!skb_queue_empty(&list)) { - rx_skb = __skb_dequeue(&list); - ret = mwifiex_recv_packet(priv, rx_skb); - if (ret == -1) - dev_err(adapter->dev, "Rx of A-MSDU failed"); - } - return 0; - } else if (rx_pkt_type == PKT_TYPE_MGMT) { + if (rx_pkt_type == PKT_TYPE_MGMT) { ret = mwifiex_process_mgmt_packet(priv, skb); if (ret) dev_err(adapter->dev, "Rx of mgmt packet failed"); diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index a6a6a53cda4..9be6544bdde 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -159,6 +159,7 @@ mwifiex_set_ht_params(struct mwifiex_private *priv, struct cfg80211_ap_settings *params) { const u8 *ht_ie; + u16 cap_info; if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) return; @@ -168,6 +169,25 @@ mwifiex_set_ht_params(struct mwifiex_private *priv, if (ht_ie) { memcpy(&bss_cfg->ht_cap, ht_ie + 2, sizeof(struct ieee80211_ht_cap)); + cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info); + memset(&bss_cfg->ht_cap.mcs, 0, + priv->adapter->number_of_antenna); + switch (GET_RXSTBC(cap_info)) { + case MWIFIEX_RX_STBC1: + /* HT_CAP 1X1 mode */ + memset(&bss_cfg->ht_cap.mcs, 0xff, 1); + break; + case MWIFIEX_RX_STBC12: /* fall through */ + case MWIFIEX_RX_STBC123: + /* HT_CAP 2X2 mode */ + memset(&bss_cfg->ht_cap.mcs, 0xff, 2); + break; + default: + dev_warn(priv->adapter->dev, + "Unsupported RX-STBC, default to 2x2\n"); + memset(&bss_cfg->ht_cap.mcs, 0xff, 2); + break; + } priv->ap_11n_enabled = 1; } else { memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index ae50e916d8f..92e77a398ec 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -166,6 +166,12 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) mwifiex_11n_ba_stream_timeout(priv, ba_timeout); } break; + case EVENT_EXT_SCAN_REPORT: + dev_dbg(adapter->dev, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + return mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + break; default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 3c74eb25492..9a56bc61cb1 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -284,27 +284,7 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, return 0; } - if (le16_to_cpu(uap_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { - struct sk_buff_head list; - struct sk_buff *rx_skb; - - __skb_queue_head_init(&list); - skb_pull(skb, le16_to_cpu(uap_rx_pd->rx_pkt_offset)); - skb_trim(skb, le16_to_cpu(uap_rx_pd->rx_pkt_length)); - - ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, - priv->wdev->iftype, 0, false); - - while (!skb_queue_empty(&list)) { - rx_skb = __skb_dequeue(&list); - ret = mwifiex_recv_packet(priv, rx_skb); - if (ret) - dev_err(adapter->dev, - "AP:Rx A-MSDU failed"); - } - - return 0; - } else if (rx_pkt_type == PKT_TYPE_MGMT) { + if (rx_pkt_type == PKT_TYPE_MGMT) { ret = mwifiex_process_mgmt_packet(priv, skb); if (ret) dev_err(adapter->dev, "Rx of mgmt packet failed"); diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 1c5f2b66f05..0a7cc742aed 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -37,8 +37,8 @@ /* Offset for TOS field in the IP header */ #define IPTOS_OFFSET 5 -static bool enable_tx_amsdu; -module_param(enable_tx_amsdu, bool, 0644); +static bool disable_tx_amsdu; +module_param(disable_tx_amsdu, bool, 0644); /* WMM information IE */ static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, @@ -413,7 +413,13 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) continue; for (i = 0; i < MAX_NUM_TID; ++i) { - priv->aggr_prio_tbl[i].amsdu = priv->tos_to_tid_inv[i]; + if (!disable_tx_amsdu && + adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K) + priv->aggr_prio_tbl[i].amsdu = + priv->tos_to_tid_inv[i]; + else + priv->aggr_prio_tbl[i].amsdu = + BA_STREAM_NOT_ALLOWED; priv->aggr_prio_tbl[i].ampdu_ap = priv->tos_to_tid_inv[i]; priv->aggr_prio_tbl[i].ampdu_user = @@ -1247,13 +1253,22 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) if (!ptr->is_11n_enabled || mwifiex_is_ba_stream_setup(priv, ptr, tid) || - priv->wps.session_enable || - ((priv->sec_info.wpa_enabled || - priv->sec_info.wpa2_enabled) && - !priv->wpa_is_gtk_set)) { - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_send_single_packet() */ + priv->wps.session_enable) { + if (ptr->is_11n_enabled && + mwifiex_is_ba_stream_setup(priv, ptr, tid) && + mwifiex_is_amsdu_in_ampdu_allowed(priv, ptr, tid) && + mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + * mwifiex_11n_aggregate_pkt() + */ + else + mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + * mwifiex_send_single_packet() + */ } else { if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && ptr->ba_pkt_count > ptr->ba_packet_thr) { @@ -1268,7 +1283,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_send_delba(priv, tid_del, ra, 1); } } - if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) && + if (mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index b7ab3dfb3de..043bd1c23c1 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -1053,6 +1053,10 @@ static int p54u_probe(struct usb_interface *intf, priv->upload_fw = p54u_upload_firmware_net2280; } err = p54u_load_firmware(dev, intf); + if (err) { + usb_put_dev(udev); + p54_free_common(dev); + } return err; } diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig new file mode 100644 index 00000000000..35245f994c1 --- /dev/null +++ b/drivers/net/wireless/rsi/Kconfig @@ -0,0 +1,30 @@ +config RSI_91X + tristate "Redpine Signals Inc 91x WLAN driver support" + depends on MAC80211 + ---help--- + This option enabes support for RSI 1x1 devices. + Select M (recommended), if you have a RSI 1x1 wireless module. + +config RSI_DEBUGFS + bool "Redpine Signals Inc debug support" + depends on RSI_91X + default y + ---help--- + Say Y, if you would like to enable debug support. This option + creates debugfs entries + +config RSI_SDIO + tristate "Redpine Signals SDIO bus support" + depends on MMC && RSI_91X + default m + ---help--- + This option enables the SDIO bus support in rsi drivers. + Select M (recommended), if you have a RSI 1x1 wireless module. + +config RSI_USB + tristate "Redpine Signals USB bus support" + depends on USB && RSI_91X + default m + ---help--- + This option enables the USB bus support in rsi drivers. + Select M (recommended), if you have a RSI 1x1 wireless module. diff --git a/drivers/net/wireless/rsi/Makefile b/drivers/net/wireless/rsi/Makefile new file mode 100644 index 00000000000..25828b69275 --- /dev/null +++ b/drivers/net/wireless/rsi/Makefile @@ -0,0 +1,12 @@ +rsi_91x-y += rsi_91x_main.o +rsi_91x-y += rsi_91x_core.o +rsi_91x-y += rsi_91x_mac80211.o +rsi_91x-y += rsi_91x_mgmt.o +rsi_91x-y += rsi_91x_pkt.o +rsi_91x-$(CONFIG_RSI_DEBUGFS) += rsi_91x_debugfs.o + +rsi_usb-y += rsi_91x_usb.o rsi_91x_usb_ops.o +rsi_sdio-y += rsi_91x_sdio.o rsi_91x_sdio_ops.o +obj-$(CONFIG_RSI_91X) += rsi_91x.o +obj-$(CONFIG_RSI_SDIO) += rsi_sdio.o +obj-$(CONFIG_RSI_USB) += rsi_usb.o diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c new file mode 100644 index 00000000000..e89535e86ca --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -0,0 +1,342 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 "rsi_mgmt.h" +#include "rsi_common.h" + +/** + * rsi_determine_min_weight_queue() - This function determines the queue with + * the min weight. + * @common: Pointer to the driver private structure. + * + * Return: q_num: Corresponding queue number. + */ +static u8 rsi_determine_min_weight_queue(struct rsi_common *common) +{ + struct wmm_qinfo *tx_qinfo = common->tx_qinfo; + u32 q_len = 0; + u8 ii = 0; + + for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { + q_len = skb_queue_len(&common->tx_queue[ii]); + if ((tx_qinfo[ii].pkt_contended) && q_len) { + common->min_weight = tx_qinfo[ii].weight; + break; + } + } + return ii; +} + +/** + * rsi_recalculate_weights() - This function recalculates the weights + * corresponding to each queue. + * @common: Pointer to the driver private structure. + * + * Return: recontend_queue bool variable + */ +static bool rsi_recalculate_weights(struct rsi_common *common) +{ + struct wmm_qinfo *tx_qinfo = common->tx_qinfo; + bool recontend_queue = false; + u8 ii = 0; + u32 q_len = 0; + + for (ii = 0; ii < NUM_EDCA_QUEUES; ii++) { + q_len = skb_queue_len(&common->tx_queue[ii]); + /* Check for the need of contention */ + if (q_len) { + if (tx_qinfo[ii].pkt_contended) { + tx_qinfo[ii].weight = + ((tx_qinfo[ii].weight > common->min_weight) ? + tx_qinfo[ii].weight - common->min_weight : 0); + } else { + tx_qinfo[ii].pkt_contended = 1; + tx_qinfo[ii].weight = tx_qinfo[ii].wme_params; + recontend_queue = true; + } + } else { /* No packets so no contention */ + tx_qinfo[ii].weight = 0; + tx_qinfo[ii].pkt_contended = 0; + } + } + + return recontend_queue; +} + +/** + * rsi_core_determine_hal_queue() - This function determines the queue from + * which packet has to be dequeued. + * @common: Pointer to the driver private structure. + * + * Return: q_num: Corresponding queue number on success. + */ +static u8 rsi_core_determine_hal_queue(struct rsi_common *common) +{ + bool recontend_queue = false; + u32 q_len = 0; + u8 q_num = INVALID_QUEUE; + u8 ii, min = 0; + + if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) { + if (!common->mgmt_q_block) + q_num = MGMT_SOFT_Q; + return q_num; + } + + if (common->pkt_cnt != 0) { + --common->pkt_cnt; + return common->selected_qnum; + } + +get_queue_num: + q_num = 0; + recontend_queue = false; + + q_num = rsi_determine_min_weight_queue(common); + q_len = skb_queue_len(&common->tx_queue[ii]); + ii = q_num; + + /* Selecting the queue with least back off */ + for (; ii < NUM_EDCA_QUEUES; ii++) { + if (((common->tx_qinfo[ii].pkt_contended) && + (common->tx_qinfo[ii].weight < min)) && q_len) { + min = common->tx_qinfo[ii].weight; + q_num = ii; + } + } + + common->tx_qinfo[q_num].pkt_contended = 0; + /* Adjust the back off values for all queues again */ + recontend_queue = rsi_recalculate_weights(common); + + q_len = skb_queue_len(&common->tx_queue[q_num]); + if (!q_len) { + /* If any queues are freshly contended and the selected queue + * doesn't have any packets + * then get the queue number again with fresh values + */ + if (recontend_queue) + goto get_queue_num; + + q_num = INVALID_QUEUE; + return q_num; + } + + common->selected_qnum = q_num; + q_len = skb_queue_len(&common->tx_queue[q_num]); + + switch (common->selected_qnum) { + case VO_Q: + if (q_len > MAX_CONTINUOUS_VO_PKTS) + common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1); + else + common->pkt_cnt = --q_len; + break; + + case VI_Q: + if (q_len > MAX_CONTINUOUS_VI_PKTS) + common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1); + else + common->pkt_cnt = --q_len; + + break; + + default: + common->pkt_cnt = 0; + break; + } + + return q_num; +} + +/** + * rsi_core_queue_pkt() - This functions enqueues the packet to the queue + * specified by the queue number. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: None. + */ +static void rsi_core_queue_pkt(struct rsi_common *common, + struct sk_buff *skb) +{ + u8 q_num = skb->priority; + if (q_num >= NUM_SOFT_QUEUES) { + rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n", + __func__, q_num); + dev_kfree_skb(skb); + return; + } + + skb_queue_tail(&common->tx_queue[q_num], skb); +} + +/** + * rsi_core_dequeue_pkt() - This functions dequeues the packet from the queue + * specified by the queue number. + * @common: Pointer to the driver private structure. + * @q_num: Queue number. + * + * Return: Pointer to sk_buff structure. + */ +static struct sk_buff *rsi_core_dequeue_pkt(struct rsi_common *common, + u8 q_num) +{ + if (q_num >= NUM_SOFT_QUEUES) { + rsi_dbg(ERR_ZONE, "%s: Invalid Queue Number: q_num = %d\n", + __func__, q_num); + return NULL; + } + + return skb_dequeue(&common->tx_queue[q_num]); +} + +/** + * rsi_core_qos_processor() - This function is used to determine the wmm queue + * based on the backoff procedure. Data packets are + * dequeued from the selected hal queue and sent to + * the below layers. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +void rsi_core_qos_processor(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + struct sk_buff *skb; + unsigned long tstamp_1, tstamp_2; + u8 q_num; + int status; + + tstamp_1 = jiffies; + while (1) { + q_num = rsi_core_determine_hal_queue(common); + rsi_dbg(DATA_TX_ZONE, + "%s: Queue number = %d\n", __func__, q_num); + + if (q_num == INVALID_QUEUE) { + rsi_dbg(DATA_TX_ZONE, "%s: No More Pkt\n", __func__); + break; + } + + mutex_lock(&common->tx_rxlock); + + status = adapter->check_hw_queue_status(adapter, q_num); + if ((status <= 0)) { + mutex_unlock(&common->tx_rxlock); + break; + } + + if ((q_num < MGMT_SOFT_Q) && + ((skb_queue_len(&common->tx_queue[q_num])) <= + MIN_DATA_QUEUE_WATER_MARK)) { + if (ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) + ieee80211_wake_queue(adapter->hw, + WME_AC(q_num)); + } + + skb = rsi_core_dequeue_pkt(common, q_num); + if (skb == NULL) { + mutex_unlock(&common->tx_rxlock); + break; + } + + if (q_num == MGMT_SOFT_Q) + status = rsi_send_mgmt_pkt(common, skb); + else + status = rsi_send_data_pkt(common, skb); + + if (status) { + mutex_unlock(&common->tx_rxlock); + break; + } + + common->tx_stats.total_tx_pkt_send[q_num]++; + + tstamp_2 = jiffies; + mutex_unlock(&common->tx_rxlock); + + if (tstamp_2 > tstamp_1 + (300 * HZ / 1000)) + schedule(); + } +} + +/** + * rsi_core_xmit() - This function transmits the packets received from mac80211 + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: None. + */ +void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_tx_info *info; + struct skb_info *tx_params; + struct ieee80211_hdr *tmp_hdr = NULL; + u8 q_num, tid = 0; + + if ((!skb) || (!skb->len)) { + rsi_dbg(ERR_ZONE, "%s: Null skb/zero Length packet\n", + __func__); + goto xmit_fail; + } + info = IEEE80211_SKB_CB(skb); + tx_params = (struct skb_info *)info->driver_data; + tmp_hdr = (struct ieee80211_hdr *)&skb->data[0]; + + if (common->fsm_state != FSM_MAC_INIT_DONE) { + rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__); + goto xmit_fail; + } + + if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) || + (ieee80211_is_ctl(tmp_hdr->frame_control))) { + q_num = MGMT_SOFT_Q; + skb->priority = q_num; + } else { + if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { + tid = (skb->data[24] & IEEE80211_QOS_TID); + skb->priority = TID_TO_WME_AC(tid); + } else { + tid = IEEE80211_NONQOS_TID; + skb->priority = BE_Q; + } + q_num = skb->priority; + tx_params->tid = tid; + tx_params->sta_id = 0; + } + + if ((q_num != MGMT_SOFT_Q) && + ((skb_queue_len(&common->tx_queue[q_num]) + 1) >= + DATA_QUEUE_WATER_MARK)) { + if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num))) + ieee80211_stop_queue(adapter->hw, WME_AC(q_num)); + rsi_set_event(&common->tx_thread.event); + goto xmit_fail; + } + + rsi_core_queue_pkt(common, skb); + rsi_dbg(DATA_TX_ZONE, "%s: ===> Scheduling TX thead <===\n", __func__); + rsi_set_event(&common->tx_thread.event); + + return; + +xmit_fail: + rsi_dbg(ERR_ZONE, "%s: Failed to queue packet\n", __func__); + /* Dropping pkt here */ + ieee80211_free_txskb(common->priv->hw, skb); +} diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c new file mode 100644 index 00000000000..7e4ef455441 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c @@ -0,0 +1,339 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 "rsi_debugfs.h" +#include "rsi_sdio.h" + +/** + * rsi_sdio_stats_read() - This function returns the sdio status of the driver. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_sdio_stats_read(struct seq_file *seq, void *data) +{ + struct rsi_common *common = seq->private; + struct rsi_hw *adapter = common->priv; + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + + seq_printf(seq, "total_sdio_interrupts: %d\n", + dev->rx_info.sdio_int_counter); + seq_printf(seq, "sdio_msdu_pending_intr_count: %d\n", + dev->rx_info.total_sdio_msdu_pending_intr); + seq_printf(seq, "sdio_buff_full_count : %d\n", + dev->rx_info.buf_full_counter); + seq_printf(seq, "sdio_buf_semi_full_count %d\n", + dev->rx_info.buf_semi_full_counter); + seq_printf(seq, "sdio_unknown_intr_count: %d\n", + dev->rx_info.total_sdio_unknown_intr); + /* RX Path Stats */ + seq_printf(seq, "BUFFER FULL STATUS : %d\n", + dev->rx_info.buffer_full); + seq_printf(seq, "SEMI BUFFER FULL STATUS : %d\n", + dev->rx_info.semi_buffer_full); + seq_printf(seq, "MGMT BUFFER FULL STATUS : %d\n", + dev->rx_info.mgmt_buffer_full); + seq_printf(seq, "BUFFER FULL COUNTER : %d\n", + dev->rx_info.buf_full_counter); + seq_printf(seq, "BUFFER SEMI FULL COUNTER : %d\n", + dev->rx_info.buf_semi_full_counter); + seq_printf(seq, "MGMT BUFFER FULL COUNTER : %d\n", + dev->rx_info.mgmt_buf_full_counter); + + return 0; +} + +/** + * rsi_sdio_stats_open() - This funtion calls single open function of seq_file + * to open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_sdio_stats_open(struct inode *inode, + struct file *file) +{ + return single_open(file, rsi_sdio_stats_read, inode->i_private); +} + +/** + * rsi_version_read() - This function gives driver and firmware version number. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_version_read(struct seq_file *seq, void *data) +{ + struct rsi_common *common = seq->private; + + common->driver_ver.major = 0; + common->driver_ver.minor = 1; + common->driver_ver.release_num = 0; + common->driver_ver.patch_num = 0; + seq_printf(seq, "Driver : %x.%d.%d.%d\nLMAC : %d.%d.%d.%d\n", + common->driver_ver.major, + common->driver_ver.minor, + common->driver_ver.release_num, + common->driver_ver.patch_num, + common->fw_ver.major, + common->fw_ver.minor, + common->fw_ver.release_num, + common->fw_ver.patch_num); + return 0; +} + +/** + * rsi_version_open() - This funtion calls single open function of seq_file to + * open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_version_open(struct inode *inode, + struct file *file) +{ + return single_open(file, rsi_version_read, inode->i_private); +} + +/** + * rsi_stats_read() - This function return the status of the driver. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_stats_read(struct seq_file *seq, void *data) +{ + struct rsi_common *common = seq->private; + + unsigned char fsm_state[][32] = { + "FSM_CARD_NOT_READY", + "FSM_BOOT_PARAMS_SENT", + "FSM_EEPROM_READ_MAC_ADDR", + "FSM_RESET_MAC_SENT", + "FSM_RADIO_CAPS_SENT", + "FSM_BB_RF_PROG_SENT", + "FSM_MAC_INIT_DONE" + }; + seq_puts(seq, "==> RSI STA DRIVER STATUS <==\n"); + seq_puts(seq, "DRIVER_FSM_STATE: "); + + if (common->fsm_state <= FSM_MAC_INIT_DONE) + seq_printf(seq, "%s", fsm_state[common->fsm_state]); + + seq_printf(seq, "(%d)\n\n", common->fsm_state); + + /* Mgmt TX Path Stats */ + seq_printf(seq, "total_mgmt_pkt_send : %d\n", + common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]); + seq_printf(seq, "total_mgmt_pkt_queued : %d\n", + skb_queue_len(&common->tx_queue[4])); + seq_printf(seq, "total_mgmt_pkt_freed : %d\n", + common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]); + + /* Data TX Path Stats */ + seq_printf(seq, "total_data_vo_pkt_send: %8d\t", + common->tx_stats.total_tx_pkt_send[VO_Q]); + seq_printf(seq, "total_data_vo_pkt_queued: %8d\t", + skb_queue_len(&common->tx_queue[0])); + seq_printf(seq, "total_vo_pkt_freed: %8d\n", + common->tx_stats.total_tx_pkt_freed[VO_Q]); + seq_printf(seq, "total_data_vi_pkt_send: %8d\t", + common->tx_stats.total_tx_pkt_send[VI_Q]); + seq_printf(seq, "total_data_vi_pkt_queued: %8d\t", + skb_queue_len(&common->tx_queue[1])); + seq_printf(seq, "total_vi_pkt_freed: %8d\n", + common->tx_stats.total_tx_pkt_freed[VI_Q]); + seq_printf(seq, "total_data_be_pkt_send: %8d\t", + common->tx_stats.total_tx_pkt_send[BE_Q]); + seq_printf(seq, "total_data_be_pkt_queued: %8d\t", + skb_queue_len(&common->tx_queue[2])); + seq_printf(seq, "total_be_pkt_freed: %8d\n", + common->tx_stats.total_tx_pkt_freed[BE_Q]); + seq_printf(seq, "total_data_bk_pkt_send: %8d\t", + common->tx_stats.total_tx_pkt_send[BK_Q]); + seq_printf(seq, "total_data_bk_pkt_queued: %8d\t", + skb_queue_len(&common->tx_queue[3])); + seq_printf(seq, "total_bk_pkt_freed: %8d\n", + common->tx_stats.total_tx_pkt_freed[BK_Q]); + + seq_puts(seq, "\n"); + return 0; +} + +/** + * rsi_stats_open() - This funtion calls single open function of seq_file to + * open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_stats_open(struct inode *inode, + struct file *file) +{ + return single_open(file, rsi_stats_read, inode->i_private); +} + +/** + * rsi_debug_zone_read() - This function display the currently enabled debug zones. + * @seq: Pointer to the sequence file structure. + * @data: Pointer to the data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_debug_zone_read(struct seq_file *seq, void *data) +{ + rsi_dbg(FSM_ZONE, "%x: rsi_enabled zone", rsi_zone_enabled); + seq_printf(seq, "The zones available are %#x\n", + rsi_zone_enabled); + return 0; +} + +/** + * rsi_debug_read() - This funtion calls single open function of seq_file to + * open file and read contents from it. + * @inode: Pointer to the inode structure. + * @file: Pointer to the file structure. + * + * Return: Pointer to the opened file status: 0 on success, ENOMEM on failure. + */ +static int rsi_debug_read(struct inode *inode, + struct file *file) +{ + return single_open(file, rsi_debug_zone_read, inode->i_private); +} + +/** + * rsi_debug_zone_write() - This function writes into hal queues as per user + * requirement. + * @filp: Pointer to the file structure. + * @buff: Pointer to the character buffer. + * @len: Length of the data to be written into buffer. + * @data: Pointer to the data. + * + * Return: len: Number of bytes read. + */ +static ssize_t rsi_debug_zone_write(struct file *filp, + const char __user *buff, + size_t len, + loff_t *data) +{ + unsigned long dbg_zone; + int ret; + + if (!len) + return 0; + + ret = kstrtoul_from_user(buff, len, 16, &dbg_zone); + + if (ret) + return ret; + + rsi_zone_enabled = dbg_zone; + return len; +} + +#define FOPS(fopen) { \ + .owner = THIS_MODULE, \ + .open = (fopen), \ + .read = seq_read, \ + .llseek = seq_lseek, \ +} + +#define FOPS_RW(fopen, fwrite) { \ + .owner = THIS_MODULE, \ + .open = (fopen), \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .write = (fwrite), \ +} + +static const struct rsi_dbg_files dev_debugfs_files[] = { + {"version", 0644, FOPS(rsi_version_open),}, + {"stats", 0644, FOPS(rsi_stats_open),}, + {"debug_zone", 0666, FOPS_RW(rsi_debug_read, rsi_debug_zone_write),}, + {"sdio_stats", 0644, FOPS(rsi_sdio_stats_open),}, +}; + +/** + * rsi_init_dbgfs() - This function initializes the dbgfs entry. + * @adapter: Pointer to the adapter structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_init_dbgfs(struct rsi_hw *adapter) +{ + struct rsi_common *common = adapter->priv; + struct rsi_debugfs *dev_dbgfs; + char devdir[6]; + int ii; + const struct rsi_dbg_files *files; + + dev_dbgfs = kzalloc(sizeof(*dev_dbgfs), GFP_KERNEL); + adapter->dfsentry = dev_dbgfs; + + snprintf(devdir, sizeof(devdir), "%s", + wiphy_name(adapter->hw->wiphy)); + dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL); + + if (IS_ERR(dev_dbgfs->subdir)) { + if (dev_dbgfs->subdir == ERR_PTR(-ENODEV)) + rsi_dbg(ERR_ZONE, + "%s:Debugfs has not been mounted\n", __func__); + else + rsi_dbg(ERR_ZONE, "debugfs:%s not created\n", devdir); + + adapter->dfsentry = NULL; + kfree(dev_dbgfs); + return (int)PTR_ERR(dev_dbgfs->subdir); + } else { + for (ii = 0; ii < adapter->num_debugfs_entries; ii++) { + files = &dev_debugfs_files[ii]; + dev_dbgfs->rsi_files[ii] = + debugfs_create_file(files->name, + files->perms, + dev_dbgfs->subdir, + common, + &files->fops); + } + } + return 0; +} +EXPORT_SYMBOL_GPL(rsi_init_dbgfs); + +/** + * rsi_remove_dbgfs() - Removes the previously created dbgfs file entries + * in the reverse order of creation. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_remove_dbgfs(struct rsi_hw *adapter) +{ + struct rsi_debugfs *dev_dbgfs = adapter->dfsentry; + + if (!dev_dbgfs) + return; + + debugfs_remove_recursive(dev_dbgfs->subdir); +} +EXPORT_SYMBOL_GPL(rsi_remove_dbgfs); diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c new file mode 100644 index 00000000000..84164747ace --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -0,0 +1,1008 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 <linux/etherdevice.h> +#include "rsi_debugfs.h" +#include "rsi_mgmt.h" +#include "rsi_common.h" + +static const struct ieee80211_channel rsi_2ghz_channels[] = { + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2412, + .hw_value = 1 }, /* Channel 1 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2417, + .hw_value = 2 }, /* Channel 2 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2422, + .hw_value = 3 }, /* Channel 3 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2427, + .hw_value = 4 }, /* Channel 4 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2432, + .hw_value = 5 }, /* Channel 5 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2437, + .hw_value = 6 }, /* Channel 6 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2442, + .hw_value = 7 }, /* Channel 7 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2447, + .hw_value = 8 }, /* Channel 8 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2452, + .hw_value = 9 }, /* Channel 9 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2457, + .hw_value = 10 }, /* Channel 10 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2462, + .hw_value = 11 }, /* Channel 11 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2467, + .hw_value = 12 }, /* Channel 12 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2472, + .hw_value = 13 }, /* Channel 13 */ + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2484, + .hw_value = 14 }, /* Channel 14 */ +}; + +static const struct ieee80211_channel rsi_5ghz_channels[] = { + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5180, + .hw_value = 36, }, /* Channel 36 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5200, + .hw_value = 40, }, /* Channel 40 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5220, + .hw_value = 44, }, /* Channel 44 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5240, + .hw_value = 48, }, /* Channel 48 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5260, + .hw_value = 52, }, /* Channel 52 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5280, + .hw_value = 56, }, /* Channel 56 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5300, + .hw_value = 60, }, /* Channel 60 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5320, + .hw_value = 64, }, /* Channel 64 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5500, + .hw_value = 100, }, /* Channel 100 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5520, + .hw_value = 104, }, /* Channel 104 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5540, + .hw_value = 108, }, /* Channel 108 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5560, + .hw_value = 112, }, /* Channel 112 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5580, + .hw_value = 116, }, /* Channel 116 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5600, + .hw_value = 120, }, /* Channel 120 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5620, + .hw_value = 124, }, /* Channel 124 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5640, + .hw_value = 128, }, /* Channel 128 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5660, + .hw_value = 132, }, /* Channel 132 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5680, + .hw_value = 136, }, /* Channel 136 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5700, + .hw_value = 140, }, /* Channel 140 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5745, + .hw_value = 149, }, /* Channel 149 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5765, + .hw_value = 153, }, /* Channel 153 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5785, + .hw_value = 157, }, /* Channel 157 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5805, + .hw_value = 161, }, /* Channel 161 */ + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5825, + .hw_value = 165, }, /* Channel 165 */ +}; + +struct ieee80211_rate rsi_rates[12] = { + { .bitrate = STD_RATE_01 * 5, .hw_value = RSI_RATE_1 }, + { .bitrate = STD_RATE_02 * 5, .hw_value = RSI_RATE_2 }, + { .bitrate = STD_RATE_5_5 * 5, .hw_value = RSI_RATE_5_5 }, + { .bitrate = STD_RATE_11 * 5, .hw_value = RSI_RATE_11 }, + { .bitrate = STD_RATE_06 * 5, .hw_value = RSI_RATE_6 }, + { .bitrate = STD_RATE_09 * 5, .hw_value = RSI_RATE_9 }, + { .bitrate = STD_RATE_12 * 5, .hw_value = RSI_RATE_12 }, + { .bitrate = STD_RATE_18 * 5, .hw_value = RSI_RATE_18 }, + { .bitrate = STD_RATE_24 * 5, .hw_value = RSI_RATE_24 }, + { .bitrate = STD_RATE_36 * 5, .hw_value = RSI_RATE_36 }, + { .bitrate = STD_RATE_48 * 5, .hw_value = RSI_RATE_48 }, + { .bitrate = STD_RATE_54 * 5, .hw_value = RSI_RATE_54 }, +}; + +const u16 rsi_mcsrates[8] = { + RSI_RATE_MCS0, RSI_RATE_MCS1, RSI_RATE_MCS2, RSI_RATE_MCS3, + RSI_RATE_MCS4, RSI_RATE_MCS5, RSI_RATE_MCS6, RSI_RATE_MCS7 +}; + +/** + * rsi_is_cipher_wep() - This function determines if the cipher is WEP or not. + * @common: Pointer to the driver private structure. + * + * Return: If cipher type is WEP, a value of 1 is returned, else 0. + */ + +bool rsi_is_cipher_wep(struct rsi_common *common) +{ + if (((common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP104) || + (common->secinfo.gtk_cipher == WLAN_CIPHER_SUITE_WEP40)) && + (!common->secinfo.ptk_cipher)) + return true; + else + return false; +} + +/** + * rsi_register_rates_channels() - This function registers channels and rates. + * @adapter: Pointer to the adapter structure. + * @band: Operating band to be set. + * + * Return: None. + */ +static void rsi_register_rates_channels(struct rsi_hw *adapter, int band) +{ + struct ieee80211_supported_band *sbands = &adapter->sbands[band]; + void *channels = NULL; + + if (band == IEEE80211_BAND_2GHZ) { + channels = kmalloc(sizeof(rsi_2ghz_channels), GFP_KERNEL); + memcpy(channels, + rsi_2ghz_channels, + sizeof(rsi_2ghz_channels)); + sbands->band = IEEE80211_BAND_2GHZ; + sbands->n_channels = ARRAY_SIZE(rsi_2ghz_channels); + sbands->bitrates = rsi_rates; + sbands->n_bitrates = ARRAY_SIZE(rsi_rates); + } else { + channels = kmalloc(sizeof(rsi_5ghz_channels), GFP_KERNEL); + memcpy(channels, + rsi_5ghz_channels, + sizeof(rsi_5ghz_channels)); + sbands->band = IEEE80211_BAND_5GHZ; + sbands->n_channels = ARRAY_SIZE(rsi_5ghz_channels); + sbands->bitrates = &rsi_rates[4]; + sbands->n_bitrates = ARRAY_SIZE(rsi_rates) - 4; + } + + sbands->channels = channels; + + memset(&sbands->ht_cap, 0, sizeof(struct ieee80211_sta_ht_cap)); + sbands->ht_cap.ht_supported = true; + sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40); + sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K; + sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + sbands->ht_cap.mcs.rx_mask[0] = 0xff; + sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + /* sbands->ht_cap.mcs.rx_highest = 0x82; */ +} + +/** + * rsi_mac80211_attach() - This function is used to de-initialize the + * Mac80211 stack. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_mac80211_detach(struct rsi_hw *adapter) +{ + struct ieee80211_hw *hw = adapter->hw; + + if (hw) { + ieee80211_stop_queues(hw); + ieee80211_unregister_hw(hw); + ieee80211_free_hw(hw); + } + + rsi_remove_dbgfs(adapter); +} +EXPORT_SYMBOL_GPL(rsi_mac80211_detach); + +/** + * rsi_indicate_tx_status() - This function indicates the transmit status. + * @adapter: Pointer to the adapter structure. + * @skb: Pointer to the socket buffer structure. + * @status: Status + * + * Return: None. + */ +void rsi_indicate_tx_status(struct rsi_hw *adapter, + struct sk_buff *skb, + int status) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + memset(info->driver_data, 0, IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + + if (!status) + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(adapter->hw, skb); +} + +/** + * rsi_mac80211_tx() - This is the handler that 802.11 module calls for each + * transmitted frame.SKB contains the buffer starting + * from the IEEE 802.11 header. + * @hw: Pointer to the ieee80211_hw structure. + * @control: Pointer to the ieee80211_tx_control structure + * @skb: Pointer to the socket buffer structure. + * + * Return: None + */ +static void rsi_mac80211_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + rsi_core_xmit(common, skb); +} + +/** + * rsi_mac80211_start() - This is first handler that 802.11 module calls, since + * the driver init is complete by then, just + * returns success. + * @hw: Pointer to the ieee80211_hw structure. + * + * Return: 0 as success. + */ +static int rsi_mac80211_start(struct ieee80211_hw *hw) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + common->iface_down = false; + mutex_unlock(&common->mutex); + + return 0; +} + +/** + * rsi_mac80211_stop() - This is the last handler that 802.11 module calls. + * @hw: Pointer to the ieee80211_hw structure. + * + * Return: None. + */ +static void rsi_mac80211_stop(struct ieee80211_hw *hw) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + common->iface_down = true; + mutex_unlock(&common->mutex); +} + +/** + * rsi_mac80211_add_interface() - This function is called when a netdevice + * attached to the hardware is enabled. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * + * Return: ret: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + int ret = -EOPNOTSUPP; + + mutex_lock(&common->mutex); + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (!adapter->sc_nvifs) { + ++adapter->sc_nvifs; + adapter->vifs[0] = vif; + ret = rsi_set_vap_capabilities(common, STA_OPMODE); + } + break; + default: + rsi_dbg(ERR_ZONE, + "%s: Interface type %d not supported\n", __func__, + vif->type); + } + mutex_unlock(&common->mutex); + + return ret; +} + +/** + * rsi_mac80211_remove_interface() - This function notifies driver that an + * interface is going down. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * + * Return: None. + */ +static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + if (vif->type == NL80211_IFTYPE_STATION) + adapter->sc_nvifs--; + + if (!memcmp(adapter->vifs[0], vif, sizeof(struct ieee80211_vif))) + adapter->vifs[0] = NULL; + mutex_unlock(&common->mutex); +} + +/** + * rsi_mac80211_config() - This function is a handler for configuration + * requests. The stack calls this function to + * change hardware configuration, e.g., channel. + * @hw: Pointer to the ieee80211_hw structure. + * @changed: Changed flags set. + * + * Return: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_config(struct ieee80211_hw *hw, + u32 changed) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + int status = -EOPNOTSUPP; + + mutex_lock(&common->mutex); + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *curchan = hw->conf.chandef.chan; + u16 channel = curchan->hw_value; + + rsi_dbg(INFO_ZONE, + "%s: Set channel: %d MHz type: %d channel_no %d\n", + __func__, curchan->center_freq, + curchan->flags, channel); + common->band = curchan->band; + status = rsi_set_channel(adapter->priv, channel); + } + mutex_unlock(&common->mutex); + + return status; +} + +/** + * rsi_get_connected_channel() - This function is used to get the current + * connected channel number. + * @adapter: Pointer to the adapter structure. + * + * Return: Current connected AP's channel number is returned. + */ +u16 rsi_get_connected_channel(struct rsi_hw *adapter) +{ + struct ieee80211_vif *vif = adapter->vifs[0]; + if (vif) { + struct ieee80211_bss_conf *bss = &vif->bss_conf; + struct ieee80211_channel *channel = bss->chandef.chan; + return channel->hw_value; + } + + return 0; +} + +/** + * rsi_mac80211_bss_info_changed() - This function is a handler for config + * requests related to BSS parameters that + * may vary during BSS's lifespan. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @bss_conf: Pointer to the ieee80211_bss_conf structure. + * @changed: Changed flags set. + * + * Return: None. + */ +static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + if (changed & BSS_CHANGED_ASSOC) { + rsi_dbg(INFO_ZONE, "%s: Changed Association status: %d\n", + __func__, bss_conf->assoc); + rsi_inform_bss_status(common, + bss_conf->assoc, + bss_conf->bssid, + bss_conf->qos, + bss_conf->aid); + } + mutex_unlock(&common->mutex); +} + +/** + * rsi_mac80211_conf_filter() - This function configure the device's RX filter. + * @hw: Pointer to the ieee80211_hw structure. + * @changed: Changed flags set. + * @total_flags: Total initial flags set. + * @multicast: Multicast. + * + * Return: None. + */ +static void rsi_mac80211_conf_filter(struct ieee80211_hw *hw, + u32 changed_flags, + u32 *total_flags, + u64 multicast) +{ + /* Not doing much here as of now */ + *total_flags &= RSI_SUPP_FILTERS; +} + +/** + * rsi_mac80211_conf_tx() - This function configures TX queue parameters + * (EDCF (aifs, cw_min, cw_max), bursting) + * for a hardware TX queue. + * @hw: Pointer to the ieee80211_hw structure + * @vif: Pointer to the ieee80211_vif structure. + * @queue: Queue number. + * @params: Pointer to ieee80211_tx_queue_params structure. + * + * Return: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + u8 idx = 0; + + if (queue >= IEEE80211_NUM_ACS) + return 0; + + rsi_dbg(INFO_ZONE, + "%s: Conf queue %d, aifs: %d, cwmin: %d cwmax: %d, txop: %d\n", + __func__, queue, params->aifs, + params->cw_min, params->cw_max, params->txop); + + mutex_lock(&common->mutex); + /* Map into the way the f/w expects */ + switch (queue) { + case IEEE80211_AC_VO: + idx = VO_Q; + break; + case IEEE80211_AC_VI: + idx = VI_Q; + break; + case IEEE80211_AC_BE: + idx = BE_Q; + break; + case IEEE80211_AC_BK: + idx = BK_Q; + break; + default: + idx = BE_Q; + break; + } + + memcpy(&common->edca_params[idx], + params, + sizeof(struct ieee80211_tx_queue_params)); + mutex_unlock(&common->mutex); + + return 0; +} + +/** + * rsi_hal_key_config() - This function loads the keys into the firmware. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @key: Pointer to the ieee80211_key_conf structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_hal_key_config(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *key) +{ + struct rsi_hw *adapter = hw->priv; + int status; + u8 key_type; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + key_type = RSI_PAIRWISE_KEY; + else + key_type = RSI_GROUP_KEY; + + rsi_dbg(ERR_ZONE, "%s: Cipher 0x%x key_type: %d key_len: %d\n", + __func__, key->cipher, key_type, key->keylen); + + if ((key->cipher == WLAN_CIPHER_SUITE_WEP104) || + (key->cipher == WLAN_CIPHER_SUITE_WEP40)) { + status = rsi_hal_load_key(adapter->priv, + key->key, + key->keylen, + RSI_PAIRWISE_KEY, + key->keyidx, + key->cipher); + if (status) + return status; + } + return rsi_hal_load_key(adapter->priv, + key->key, + key->keylen, + key_type, + key->keyidx, + key->cipher); +} + +/** + * rsi_mac80211_set_key() - This function sets type of key to be loaded. + * @hw: Pointer to the ieee80211_hw structure. + * @cmd: enum set_key_cmd. + * @vif: Pointer to the ieee80211_vif structure. + * @sta: Pointer to the ieee80211_sta structure. + * @key: Pointer to the ieee80211_key_conf structure. + * + * Return: status: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + struct security_info *secinfo = &common->secinfo; + int status; + + mutex_lock(&common->mutex); + switch (cmd) { + case SET_KEY: + secinfo->security_enable = true; + status = rsi_hal_key_config(hw, vif, key); + if (status) { + mutex_unlock(&common->mutex); + return status; + } + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + secinfo->ptk_cipher = key->cipher; + else + secinfo->gtk_cipher = key->cipher; + + key->hw_key_idx = key->keyidx; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + + rsi_dbg(ERR_ZONE, "%s: RSI set_key\n", __func__); + break; + + case DISABLE_KEY: + secinfo->security_enable = false; + rsi_dbg(ERR_ZONE, "%s: RSI del key\n", __func__); + memset(key, 0, sizeof(struct ieee80211_key_conf)); + status = rsi_hal_key_config(hw, vif, key); + break; + + default: + status = -EOPNOTSUPP; + break; + } + + mutex_unlock(&common->mutex); + return status; +} + +/** + * rsi_mac80211_ampdu_action() - This function selects the AMPDU action for + * the corresponding mlme_action flag and + * informs the f/w regarding this. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @action: ieee80211_ampdu_mlme_action enum. + * @sta: Pointer to the ieee80211_sta structure. + * @tid: Traffic identifier. + * @ssn: Pointer to ssn value. + * @buf_size: Buffer size (for kernel version > 2.6.38). + * + * Return: status: 0 on success, negative error code on failure. + */ +static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + unsigned short tid, + unsigned short *ssn, + unsigned char buf_size) +{ + int status = -EOPNOTSUPP; + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + u16 seq_no = 0; + u8 ii = 0; + + for (ii = 0; ii < RSI_MAX_VIFS; ii++) { + if (vif == adapter->vifs[ii]) + break; + } + + mutex_lock(&common->mutex); + rsi_dbg(INFO_ZONE, "%s: AMPDU action %d called\n", __func__, action); + if (ssn != NULL) + seq_no = *ssn; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + status = rsi_send_aggregation_params_frame(common, + tid, + seq_no, + buf_size, + STA_RX_ADDBA_DONE); + break; + + case IEEE80211_AMPDU_RX_STOP: + status = rsi_send_aggregation_params_frame(common, + tid, + 0, + buf_size, + STA_RX_DELBA); + break; + + case IEEE80211_AMPDU_TX_START: + common->vif_info[ii].seq_start = seq_no; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + status = rsi_send_aggregation_params_frame(common, + tid, + seq_no, + buf_size, + STA_TX_DELBA); + if (!status) + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + + case IEEE80211_AMPDU_TX_OPERATIONAL: + status = rsi_send_aggregation_params_frame(common, + tid, + common->vif_info[ii] + .seq_start, + buf_size, + STA_TX_ADDBA_DONE); + break; + + default: + rsi_dbg(ERR_ZONE, "%s: Uknown AMPDU action\n", __func__); + break; + } + + mutex_unlock(&common->mutex); + return status; +} + +/** + * rsi_mac80211_set_rts_threshold() - This function sets rts threshold value. + * @hw: Pointer to the ieee80211_hw structure. + * @value: Rts threshold value. + * + * Return: 0 on success. + */ +static int rsi_mac80211_set_rts_threshold(struct ieee80211_hw *hw, + u32 value) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + common->rts_threshold = value; + mutex_unlock(&common->mutex); + + return 0; +} + +/** + * rsi_mac80211_set_rate_mask() - This function sets bitrate_mask to be used. + * @hw: Pointer to the ieee80211_hw structure + * @vif: Pointer to the ieee80211_vif structure. + * @mask: Pointer to the cfg80211_bitrate_mask structure. + * + * Return: 0 on success. + */ +static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + + common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0; + + if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) { + common->fixedrate_mask[IEEE80211_BAND_2GHZ] = + (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12); + } else { + common->fixedrate_mask[IEEE80211_BAND_2GHZ] = + mask->control[IEEE80211_BAND_2GHZ].legacy; + } + mutex_unlock(&common->mutex); + + return 0; +} + +/** + * rsi_fill_rx_status() - This function fills rx status in + * ieee80211_rx_status structure. + * @hw: Pointer to the ieee80211_hw structure. + * @skb: Pointer to the socket buffer structure. + * @common: Pointer to the driver private structure. + * @rxs: Pointer to the ieee80211_rx_status structure. + * + * Return: None. + */ +static void rsi_fill_rx_status(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct rsi_common *common, + struct ieee80211_rx_status *rxs) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct skb_info *rx_params = (struct skb_info *)info->driver_data; + struct ieee80211_hdr *hdr; + char rssi = rx_params->rssi; + u8 hdrlen = 0; + u8 channel = rx_params->channel; + s32 freq; + + hdr = ((struct ieee80211_hdr *)(skb->data)); + hdrlen = ieee80211_hdrlen(hdr->frame_control); + + memset(info, 0, sizeof(struct ieee80211_tx_info)); + + rxs->signal = -(rssi); + + if (channel <= 14) + rxs->band = IEEE80211_BAND_2GHZ; + else + rxs->band = IEEE80211_BAND_5GHZ; + + freq = ieee80211_channel_to_frequency(channel, rxs->band); + + if (freq) + rxs->freq = freq; + + if (ieee80211_has_protected(hdr->frame_control)) { + if (rsi_is_cipher_wep(common)) { + memmove(skb->data + 4, skb->data, hdrlen); + skb_pull(skb, 4); + } else { + memmove(skb->data + 8, skb->data, hdrlen); + skb_pull(skb, 8); + rxs->flag |= RX_FLAG_MMIC_STRIPPED; + } + rxs->flag |= RX_FLAG_DECRYPTED; + rxs->flag |= RX_FLAG_IV_STRIPPED; + } +} + +/** + * rsi_indicate_pkt_to_os() - This function sends recieved packet to mac80211. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: None. + */ +void rsi_indicate_pkt_to_os(struct rsi_common *common, + struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_hw *hw = adapter->hw; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + + if ((common->iface_down) || (!adapter->sc_nvifs)) { + dev_kfree_skb(skb); + return; + } + + /* filling in the ieee80211_rx_status flags */ + rsi_fill_rx_status(hw, skb, common, rx_status); + + ieee80211_rx_irqsafe(hw, skb); +} + +static void rsi_set_min_rate(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct rsi_common *common) +{ + u8 band = hw->conf.chandef.chan->band; + u8 ii; + u32 rate_bitmap; + bool matched = false; + + common->bitrate_mask[band] = sta->supp_rates[band]; + + rate_bitmap = (common->fixedrate_mask[band] & sta->supp_rates[band]); + + if (rate_bitmap & 0xfff) { + /* Find out the min rate */ + for (ii = 0; ii < ARRAY_SIZE(rsi_rates); ii++) { + if (rate_bitmap & BIT(ii)) { + common->min_rate = rsi_rates[ii].hw_value; + matched = true; + break; + } + } + } + + common->vif_info[0].is_ht = sta->ht_cap.ht_supported; + + if ((common->vif_info[0].is_ht) && (rate_bitmap >> 12)) { + for (ii = 0; ii < ARRAY_SIZE(rsi_mcsrates); ii++) { + if ((rate_bitmap >> 12) & BIT(ii)) { + common->min_rate = rsi_mcsrates[ii]; + matched = true; + break; + } + } + } + + if (!matched) + common->min_rate = 0xffff; +} + +/** + * rsi_mac80211_sta_add() - This function notifies driver about a peer getting + * connected. + * @hw: pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @sta: Pointer to the ieee80211_sta structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mac80211_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + + rsi_set_min_rate(hw, sta, common); + + if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) || + (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40)) { + common->vif_info[0].sgi = true; + } + + if (sta->ht_cap.ht_supported) + ieee80211_start_tx_ba_session(sta, 0, 0); + + mutex_unlock(&common->mutex); + + return 0; +} + +/** + * rsi_mac80211_sta_remove() - This function notifies driver about a peer + * getting disconnected. + * @hw: Pointer to the ieee80211_hw structure. + * @vif: Pointer to the ieee80211_vif structure. + * @sta: Pointer to the ieee80211_sta structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mac80211_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rsi_hw *adapter = hw->priv; + struct rsi_common *common = adapter->priv; + + mutex_lock(&common->mutex); + /* Resetting all the fields to default values */ + common->bitrate_mask[IEEE80211_BAND_2GHZ] = 0; + common->bitrate_mask[IEEE80211_BAND_5GHZ] = 0; + common->min_rate = 0xffff; + common->vif_info[0].is_ht = false; + common->vif_info[0].sgi = false; + common->vif_info[0].seq_start = 0; + common->secinfo.ptk_cipher = 0; + common->secinfo.gtk_cipher = 0; + mutex_unlock(&common->mutex); + + return 0; +} + +static struct ieee80211_ops mac80211_ops = { + .tx = rsi_mac80211_tx, + .start = rsi_mac80211_start, + .stop = rsi_mac80211_stop, + .add_interface = rsi_mac80211_add_interface, + .remove_interface = rsi_mac80211_remove_interface, + .config = rsi_mac80211_config, + .bss_info_changed = rsi_mac80211_bss_info_changed, + .conf_tx = rsi_mac80211_conf_tx, + .configure_filter = rsi_mac80211_conf_filter, + .set_key = rsi_mac80211_set_key, + .set_rts_threshold = rsi_mac80211_set_rts_threshold, + .set_bitrate_mask = rsi_mac80211_set_rate_mask, + .ampdu_action = rsi_mac80211_ampdu_action, + .sta_add = rsi_mac80211_sta_add, + .sta_remove = rsi_mac80211_sta_remove, +}; + +/** + * rsi_mac80211_attach() - This function is used to initialize Mac80211 stack. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_mac80211_attach(struct rsi_common *common) +{ + int status = 0; + struct ieee80211_hw *hw = NULL; + struct wiphy *wiphy = NULL; + struct rsi_hw *adapter = common->priv; + u8 addr_mask[ETH_ALEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x3}; + + rsi_dbg(INIT_ZONE, "%s: Performing mac80211 attach\n", __func__); + + hw = ieee80211_alloc_hw(sizeof(struct rsi_hw), &mac80211_ops); + if (!hw) { + rsi_dbg(ERR_ZONE, "%s: ieee80211 hw alloc failed\n", __func__); + return -ENOMEM; + } + + wiphy = hw->wiphy; + + SET_IEEE80211_DEV(hw, adapter->device); + + hw->priv = adapter; + adapter->hw = hw; + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_HAS_RATE_CONTROL | + IEEE80211_HW_AMPDU_AGGREGATION | + 0; + + hw->queues = MAX_HW_QUEUES; + hw->extra_tx_headroom = RSI_NEEDED_HEADROOM; + + hw->max_rates = 1; + hw->max_rate_tries = MAX_RETRIES; + + hw->max_tx_aggregation_subframes = 6; + rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ); + hw->rate_control_algorithm = "AARF"; + + SET_IEEE80211_PERM_ADDR(hw, common->mac_addr); + ether_addr_copy(hw->wiphy->addr_mask, addr_mask); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->retry_short = RETRY_SHORT; + wiphy->retry_long = RETRY_LONG; + wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + wiphy->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + wiphy->flags = 0; + + wiphy->available_antennas_rx = 1; + wiphy->available_antennas_tx = 1; + wiphy->bands[IEEE80211_BAND_2GHZ] = + &adapter->sbands[IEEE80211_BAND_2GHZ]; + + status = ieee80211_register_hw(hw); + if (status) + return status; + + return rsi_init_dbgfs(adapter); +} diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c new file mode 100644 index 00000000000..8810862ae82 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_main.c @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2014 Redpine Signals 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/firmware.h> +#include "rsi_mgmt.h" +#include "rsi_common.h" + +u32 rsi_zone_enabled = /* INFO_ZONE | + INIT_ZONE | + MGMT_TX_ZONE | + MGMT_RX_ZONE | + DATA_TX_ZONE | + DATA_RX_ZONE | + FSM_ZONE | + ISR_ZONE | */ + ERR_ZONE | + 0; +EXPORT_SYMBOL_GPL(rsi_zone_enabled); + +/** + * rsi_dbg() - This function outputs informational messages. + * @zone: Zone of interest for output message. + * @fmt: printf-style format for output message. + * + * Return: none + */ +void rsi_dbg(u32 zone, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (zone & rsi_zone_enabled) + pr_info("%pV", &vaf); + va_end(args); +} +EXPORT_SYMBOL_GPL(rsi_dbg); + +/** + * rsi_prepare_skb() - This function prepares the skb. + * @common: Pointer to the driver private structure. + * @buffer: Pointer to the packet data. + * @pkt_len: Length of the packet. + * @extended_desc: Extended descriptor. + * + * Return: Successfully skb. + */ +static struct sk_buff *rsi_prepare_skb(struct rsi_common *common, + u8 *buffer, + u32 pkt_len, + u8 extended_desc) +{ + struct ieee80211_tx_info *info; + struct skb_info *rx_params; + struct sk_buff *skb = NULL; + u8 payload_offset; + + if (WARN(!pkt_len, "%s: Dummy pkt received", __func__)) + return NULL; + + if (pkt_len > (RSI_RCV_BUFFER_LEN * 4)) { + rsi_dbg(ERR_ZONE, "%s: Pkt size > max rx buf size %d\n", + __func__, pkt_len); + pkt_len = RSI_RCV_BUFFER_LEN * 4; + } + + pkt_len -= extended_desc; + skb = dev_alloc_skb(pkt_len + FRAME_DESC_SZ); + if (skb == NULL) + return NULL; + + payload_offset = (extended_desc + FRAME_DESC_SZ); + skb_put(skb, pkt_len); + memcpy((skb->data), (buffer + payload_offset), skb->len); + + info = IEEE80211_SKB_CB(skb); + rx_params = (struct skb_info *)info->driver_data; + rx_params->rssi = rsi_get_rssi(buffer); + rx_params->channel = rsi_get_connected_channel(common->priv); + + return skb; +} + +/** + * rsi_read_pkt() - This function reads frames from the card. + * @common: Pointer to the driver private structure. + * @rcv_pkt_len: Received pkt length. In case of USB it is 0. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len) +{ + u8 *frame_desc = NULL, extended_desc = 0; + u32 index, length = 0, queueno = 0; + u16 actual_length = 0, offset; + struct sk_buff *skb = NULL; + + index = 0; + do { + frame_desc = &common->rx_data_pkt[index]; + actual_length = *(u16 *)&frame_desc[0]; + offset = *(u16 *)&frame_desc[2]; + + queueno = rsi_get_queueno(frame_desc, offset); + length = rsi_get_length(frame_desc, offset); + extended_desc = rsi_get_extended_desc(frame_desc, offset); + + switch (queueno) { + case RSI_WIFI_DATA_Q: + skb = rsi_prepare_skb(common, + (frame_desc + offset), + length, + extended_desc); + if (skb == NULL) + goto fail; + + rsi_indicate_pkt_to_os(common, skb); + break; + + case RSI_WIFI_MGMT_Q: + rsi_mgmt_pkt_recv(common, (frame_desc + offset)); + break; + + default: + rsi_dbg(ERR_ZONE, "%s: pkt from invalid queue: %d\n", + __func__, queueno); + goto fail; + } + + index += actual_length; + rcv_pkt_len -= actual_length; + } while (rcv_pkt_len > 0); + + return 0; +fail: + return -EINVAL; +} +EXPORT_SYMBOL_GPL(rsi_read_pkt); + +/** + * rsi_tx_scheduler_thread() - This function is a kernel thread to send the + * packets to the device. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +static void rsi_tx_scheduler_thread(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + u32 timeout = EVENT_WAIT_FOREVER; + + do { + if (adapter->determine_event_timeout) + timeout = adapter->determine_event_timeout(adapter); + rsi_wait_event(&common->tx_thread.event, timeout); + rsi_reset_event(&common->tx_thread.event); + + if (common->init_done) + rsi_core_qos_processor(common); + } while (atomic_read(&common->tx_thread.thread_done) == 0); + complete_and_exit(&common->tx_thread.completion, 0); +} + +/** + * rsi_91x_init() - This function initializes os interface operations. + * @void: Void. + * + * Return: Pointer to the adapter structure on success, NULL on failure . + */ +struct rsi_hw *rsi_91x_init(void) +{ + struct rsi_hw *adapter = NULL; + struct rsi_common *common = NULL; + u8 ii = 0; + + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); + if (!adapter) + return NULL; + + adapter->priv = kzalloc(sizeof(*common), GFP_KERNEL); + if (adapter->priv == NULL) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of memory\n", + __func__); + kfree(adapter); + return NULL; + } else { + common = adapter->priv; + common->priv = adapter; + } + + for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) + skb_queue_head_init(&common->tx_queue[ii]); + + rsi_init_event(&common->tx_thread.event); + mutex_init(&common->mutex); + mutex_init(&common->tx_rxlock); + + if (rsi_create_kthread(common, + &common->tx_thread, + rsi_tx_scheduler_thread, + "Tx-Thread")) { + rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n", __func__); + goto err; + } + + common->init_done = true; + return adapter; + +err: + kfree(common); + kfree(adapter); + return NULL; +} +EXPORT_SYMBOL_GPL(rsi_91x_init); + +/** + * rsi_91x_deinit() - This function de-intializes os intf operations. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_91x_deinit(struct rsi_hw *adapter) +{ + struct rsi_common *common = adapter->priv; + u8 ii; + + rsi_dbg(INFO_ZONE, "%s: Performing deinit os ops\n", __func__); + + rsi_kill_thread(&common->tx_thread); + + for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) + skb_queue_purge(&common->tx_queue[ii]); + + common->init_done = false; + + kfree(common); + kfree(adapter->rsi_dev); + kfree(adapter); +} +EXPORT_SYMBOL_GPL(rsi_91x_deinit); + +/** + * rsi_91x_hal_module_init() - This function is invoked when the module is + * loaded into the kernel. + * It registers the client driver. + * @void: Void. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_91x_hal_module_init(void) +{ + rsi_dbg(INIT_ZONE, "%s: Module init called\n", __func__); + return 0; +} + +/** + * rsi_91x_hal_module_exit() - This function is called at the time of + * removing/unloading the module. + * It unregisters the client driver. + * @void: Void. + * + * Return: None. + */ +static void rsi_91x_hal_module_exit(void) +{ + rsi_dbg(INIT_ZONE, "%s: Module exit called\n", __func__); +} + +module_init(rsi_91x_hal_module_init); +module_exit(rsi_91x_hal_module_exit); +MODULE_AUTHOR("Redpine Signals Inc"); +MODULE_DESCRIPTION("Station driver for RSI 91x devices"); +MODULE_SUPPORTED_DEVICE("RSI-91x"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c new file mode 100644 index 00000000000..ef37d4b27bd --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -0,0 +1,1303 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 <linux/etherdevice.h> +#include "rsi_mgmt.h" +#include "rsi_common.h" + +static struct bootup_params boot_params_20 = { + .magic_number = cpu_to_le16(0x5aa5), + .crystal_good_time = 0x0, + .valid = cpu_to_le32(VALID_20), + .reserved_for_valids = 0x0, + .bootup_mode_info = 0x0, + .digital_loop_back_params = 0x0, + .rtls_timestamp_en = 0x0, + .host_spi_intr_cfg = 0x0, + .device_clk_info = {{ + .pll_config_g = { + .tapll_info_g = { + .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)| + (TA_PLL_M_VAL_20)), + .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20), + }, + .pll960_info_g = { + .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)| + (PLL960_N_VAL_20)), + .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20), + .pll_reg_3 = 0x0, + }, + .afepll_info_g = { + .pll_reg = cpu_to_le16(0x9f0), + } + }, + .switch_clk_g = { + .switch_clk_info = cpu_to_le16(BIT(3)), + .bbp_lmac_clk_reg_val = cpu_to_le16(0x121), + .umac_clock_reg_config = 0x0, + .qspi_uart_clock_reg_config = 0x0 + } + }, + { + .pll_config_g = { + .tapll_info_g = { + .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)| + (TA_PLL_M_VAL_20)), + .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20), + }, + .pll960_info_g = { + .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)| + (PLL960_N_VAL_20)), + .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20), + .pll_reg_3 = 0x0, + }, + .afepll_info_g = { + .pll_reg = cpu_to_le16(0x9f0), + } + }, + .switch_clk_g = { + .switch_clk_info = 0x0, + .bbp_lmac_clk_reg_val = 0x0, + .umac_clock_reg_config = 0x0, + .qspi_uart_clock_reg_config = 0x0 + } + }, + { + .pll_config_g = { + .tapll_info_g = { + .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_20 << 8)| + (TA_PLL_M_VAL_20)), + .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_20), + }, + .pll960_info_g = { + .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_20 << 8)| + (PLL960_N_VAL_20)), + .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_20), + .pll_reg_3 = 0x0, + }, + .afepll_info_g = { + .pll_reg = cpu_to_le16(0x9f0), + } + }, + .switch_clk_g = { + .switch_clk_info = 0x0, + .bbp_lmac_clk_reg_val = 0x0, + .umac_clock_reg_config = 0x0, + .qspi_uart_clock_reg_config = 0x0 + } + } }, + .buckboost_wakeup_cnt = 0x0, + .pmu_wakeup_wait = 0x0, + .shutdown_wait_time = 0x0, + .pmu_slp_clkout_sel = 0x0, + .wdt_prog_value = 0x0, + .wdt_soc_rst_delay = 0x0, + .dcdc_operation_mode = 0x0, + .soc_reset_wait_cnt = 0x0 +}; + +static struct bootup_params boot_params_40 = { + .magic_number = cpu_to_le16(0x5aa5), + .crystal_good_time = 0x0, + .valid = cpu_to_le32(VALID_40), + .reserved_for_valids = 0x0, + .bootup_mode_info = 0x0, + .digital_loop_back_params = 0x0, + .rtls_timestamp_en = 0x0, + .host_spi_intr_cfg = 0x0, + .device_clk_info = {{ + .pll_config_g = { + .tapll_info_g = { + .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)| + (TA_PLL_M_VAL_40)), + .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40), + }, + .pll960_info_g = { + .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)| + (PLL960_N_VAL_40)), + .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40), + .pll_reg_3 = 0x0, + }, + .afepll_info_g = { + .pll_reg = cpu_to_le16(0x9f0), + } + }, + .switch_clk_g = { + .switch_clk_info = cpu_to_le16(0x09), + .bbp_lmac_clk_reg_val = cpu_to_le16(0x1121), + .umac_clock_reg_config = cpu_to_le16(0x48), + .qspi_uart_clock_reg_config = 0x0 + } + }, + { + .pll_config_g = { + .tapll_info_g = { + .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)| + (TA_PLL_M_VAL_40)), + .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40), + }, + .pll960_info_g = { + .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)| + (PLL960_N_VAL_40)), + .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40), + .pll_reg_3 = 0x0, + }, + .afepll_info_g = { + .pll_reg = cpu_to_le16(0x9f0), + } + }, + .switch_clk_g = { + .switch_clk_info = 0x0, + .bbp_lmac_clk_reg_val = 0x0, + .umac_clock_reg_config = 0x0, + .qspi_uart_clock_reg_config = 0x0 + } + }, + { + .pll_config_g = { + .tapll_info_g = { + .pll_reg_1 = cpu_to_le16((TA_PLL_N_VAL_40 << 8)| + (TA_PLL_M_VAL_40)), + .pll_reg_2 = cpu_to_le16(TA_PLL_P_VAL_40), + }, + .pll960_info_g = { + .pll_reg_1 = cpu_to_le16((PLL960_P_VAL_40 << 8)| + (PLL960_N_VAL_40)), + .pll_reg_2 = cpu_to_le16(PLL960_M_VAL_40), + .pll_reg_3 = 0x0, + }, + .afepll_info_g = { + .pll_reg = cpu_to_le16(0x9f0), + } + }, + .switch_clk_g = { + .switch_clk_info = 0x0, + .bbp_lmac_clk_reg_val = 0x0, + .umac_clock_reg_config = 0x0, + .qspi_uart_clock_reg_config = 0x0 + } + } }, + .buckboost_wakeup_cnt = 0x0, + .pmu_wakeup_wait = 0x0, + .shutdown_wait_time = 0x0, + .pmu_slp_clkout_sel = 0x0, + .wdt_prog_value = 0x0, + .wdt_soc_rst_delay = 0x0, + .dcdc_operation_mode = 0x0, + .soc_reset_wait_cnt = 0x0 +}; + +static u16 mcs[] = {13, 26, 39, 52, 78, 104, 117, 130}; + +/** + * rsi_set_default_parameters() - This function sets default parameters. + * @common: Pointer to the driver private structure. + * + * Return: none + */ +static void rsi_set_default_parameters(struct rsi_common *common) +{ + common->band = IEEE80211_BAND_2GHZ; + common->channel_width = BW_20MHZ; + common->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + common->channel = 1; + common->min_rate = 0xffff; + common->fsm_state = FSM_CARD_NOT_READY; + common->iface_down = true; +} + +/** + * rsi_set_contention_vals() - This function sets the contention values for the + * backoff procedure. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +static void rsi_set_contention_vals(struct rsi_common *common) +{ + u8 ii = 0; + + for (; ii < NUM_EDCA_QUEUES; ii++) { + common->tx_qinfo[ii].wme_params = + (((common->edca_params[ii].cw_min / 2) + + (common->edca_params[ii].aifs)) * + WMM_SHORT_SLOT_TIME + SIFS_DURATION); + common->tx_qinfo[ii].weight = common->tx_qinfo[ii].wme_params; + common->tx_qinfo[ii].pkt_contended = 0; + } +} + +/** + * rsi_send_internal_mgmt_frame() - This function sends management frames to + * firmware.Also schedules packet to queue + * for transmission. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_send_internal_mgmt_frame(struct rsi_common *common, + struct sk_buff *skb) +{ + struct skb_info *tx_params; + + if (skb == NULL) { + rsi_dbg(ERR_ZONE, "%s: Unable to allocate skb\n", __func__); + return -ENOMEM; + } + tx_params = (struct skb_info *)&IEEE80211_SKB_CB(skb)->driver_data; + tx_params->flags |= INTERNAL_MGMT_PKT; + skb_queue_tail(&common->tx_queue[MGMT_SOFT_Q], skb); + rsi_set_event(&common->tx_thread.event); + return 0; +} + +/** + * rsi_load_radio_caps() - This function is used to send radio capabilities + * values to firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +static int rsi_load_radio_caps(struct rsi_common *common) +{ + struct rsi_radio_caps *radio_caps; + struct rsi_hw *adapter = common->priv; + struct ieee80211_hw *hw = adapter->hw; + u16 inx = 0; + u8 ii; + u8 radio_id = 0; + u16 gc[20] = {0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0}; + struct ieee80211_conf *conf = &hw->conf; + struct sk_buff *skb; + + rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__); + + skb = dev_alloc_skb(sizeof(struct rsi_radio_caps)); + + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, sizeof(struct rsi_radio_caps)); + radio_caps = (struct rsi_radio_caps *)skb->data; + + radio_caps->desc_word[1] = cpu_to_le16(RADIO_CAPABILITIES); + radio_caps->desc_word[4] = cpu_to_le16(RSI_RF_TYPE << 8); + + if (common->channel_width == BW_40MHZ) { + radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ); + radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ); + if (common->channel_width) { + radio_caps->desc_word[5] = + cpu_to_le16(common->channel_width << 12); + radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE); + } + + if (conf_is_ht40_minus(conf)) { + radio_caps->desc_word[5] = 0; + radio_caps->desc_word[5] |= + cpu_to_le16(LOWER_20_ENABLE); + radio_caps->desc_word[5] |= + cpu_to_le16(LOWER_20_ENABLE >> 12); + } + + if (conf_is_ht40_plus(conf)) { + radio_caps->desc_word[5] = 0; + radio_caps->desc_word[5] |= + cpu_to_le16(UPPER_20_ENABLE); + radio_caps->desc_word[5] |= + cpu_to_le16(UPPER_20_ENABLE >> 12); + } + } + + radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8); + + for (ii = 0; ii < MAX_HW_QUEUES; ii++) { + radio_caps->qos_params[ii].cont_win_min_q = cpu_to_le16(3); + radio_caps->qos_params[ii].cont_win_max_q = cpu_to_le16(0x3f); + radio_caps->qos_params[ii].aifsn_val_q = cpu_to_le16(2); + radio_caps->qos_params[ii].txop_q = 0; + } + + for (ii = 0; ii < MAX_HW_QUEUES - 4; ii++) { + radio_caps->qos_params[ii].cont_win_min_q = + cpu_to_le16(common->edca_params[ii].cw_min); + radio_caps->qos_params[ii].cont_win_max_q = + cpu_to_le16(common->edca_params[ii].cw_max); + radio_caps->qos_params[ii].aifsn_val_q = + cpu_to_le16((common->edca_params[ii].aifs) << 8); + radio_caps->qos_params[ii].txop_q = + cpu_to_le16(common->edca_params[ii].txop); + } + + memcpy(&common->rate_pwr[0], &gc[0], 40); + for (ii = 0; ii < 20; ii++) + radio_caps->gcpd_per_rate[inx++] = + cpu_to_le16(common->rate_pwr[ii] & 0x00FF); + + radio_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_radio_caps) - + FRAME_DESC_SZ) | + (RSI_WIFI_MGMT_Q << 12)); + + + skb_put(skb, (sizeof(struct rsi_radio_caps))); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_mgmt_pkt_to_core() - This function is the entry point for Mgmt module. + * @common: Pointer to the driver private structure. + * @msg: Pointer to received packet. + * @msg_len: Length of the recieved packet. + * @type: Type of recieved packet. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_mgmt_pkt_to_core(struct rsi_common *common, + u8 *msg, + s32 msg_len, + u8 type) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_tx_info *info; + struct skb_info *rx_params; + u8 pad_bytes = msg[4]; + u8 pkt_recv; + struct sk_buff *skb; + char *buffer; + + if (type == RX_DOT11_MGMT) { + if (!adapter->sc_nvifs) + return -ENOLINK; + + msg_len -= pad_bytes; + if ((msg_len <= 0) || (!msg)) { + rsi_dbg(MGMT_RX_ZONE, + "%s: Invalid rx msg of len = %d\n", + __func__, msg_len); + return -EINVAL; + } + + skb = dev_alloc_skb(msg_len); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed to allocate skb\n", + __func__); + return -ENOMEM; + } + + buffer = skb_put(skb, msg_len); + + memcpy(buffer, + (u8 *)(msg + FRAME_DESC_SZ + pad_bytes), + msg_len); + + pkt_recv = buffer[0]; + + info = IEEE80211_SKB_CB(skb); + rx_params = (struct skb_info *)info->driver_data; + rx_params->rssi = rsi_get_rssi(msg); + rx_params->channel = rsi_get_channel(msg); + rsi_indicate_pkt_to_os(common, skb); + } else { + rsi_dbg(MGMT_TX_ZONE, "%s: Internal Packet\n", __func__); + } + + return 0; +} + +/** + * rsi_hal_send_sta_notify_frame() - This function sends the station notify + * frame to firmware. + * @common: Pointer to the driver private structure. + * @opmode: Operating mode of device. + * @notify_event: Notification about station connection. + * @bssid: bssid. + * @qos_enable: Qos is enabled. + * @aid: Aid (unique for all STA). + * + * Return: status: 0 on success, corresponding negative error code on failure. + */ +static int rsi_hal_send_sta_notify_frame(struct rsi_common *common, + u8 opmode, + u8 notify_event, + const unsigned char *bssid, + u8 qos_enable, + u16 aid) +{ + struct sk_buff *skb = NULL; + struct rsi_peer_notify *peer_notify; + u16 vap_id = 0; + int status; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending sta notify frame\n", __func__); + + skb = dev_alloc_skb(sizeof(struct rsi_peer_notify)); + + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, sizeof(struct rsi_peer_notify)); + peer_notify = (struct rsi_peer_notify *)skb->data; + + peer_notify->command = cpu_to_le16(opmode << 1); + + switch (notify_event) { + case STA_CONNECTED: + peer_notify->command |= cpu_to_le16(RSI_ADD_PEER); + break; + case STA_DISCONNECTED: + peer_notify->command |= cpu_to_le16(RSI_DELETE_PEER); + break; + default: + break; + } + + peer_notify->command |= cpu_to_le16((aid & 0xfff) << 4); + ether_addr_copy(peer_notify->mac_addr, bssid); + + peer_notify->sta_flags = cpu_to_le32((qos_enable) ? 1 : 0); + + peer_notify->desc_word[0] = + cpu_to_le16((sizeof(struct rsi_peer_notify) - FRAME_DESC_SZ) | + (RSI_WIFI_MGMT_Q << 12)); + peer_notify->desc_word[1] = cpu_to_le16(PEER_NOTIFY); + peer_notify->desc_word[7] |= cpu_to_le16(vap_id << 8); + + skb_put(skb, sizeof(struct rsi_peer_notify)); + + status = rsi_send_internal_mgmt_frame(common, skb); + + if (!status && qos_enable) { + rsi_set_contention_vals(common); + status = rsi_load_radio_caps(common); + } + return status; +} + +/** + * rsi_send_aggregation_params_frame() - This function sends the ampdu + * indication frame to firmware. + * @common: Pointer to the driver private structure. + * @tid: traffic identifier. + * @ssn: ssn. + * @buf_size: buffer size. + * @event: notification about station connection. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +int rsi_send_aggregation_params_frame(struct rsi_common *common, + u16 tid, + u16 ssn, + u8 buf_size, + u8 event) +{ + struct sk_buff *skb = NULL; + struct rsi_mac_frame *mgmt_frame; + u8 peer_id = 0; + + skb = dev_alloc_skb(FRAME_DESC_SZ); + + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + mgmt_frame = (struct rsi_mac_frame *)skb->data; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending AMPDU indication frame\n", __func__); + + mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + mgmt_frame->desc_word[1] = cpu_to_le16(AMPDU_IND); + + if (event == STA_TX_ADDBA_DONE) { + mgmt_frame->desc_word[4] = cpu_to_le16(ssn); + mgmt_frame->desc_word[5] = cpu_to_le16(buf_size); + mgmt_frame->desc_word[7] = + cpu_to_le16((tid | (START_AMPDU_AGGR << 4) | (peer_id << 8))); + } else if (event == STA_RX_ADDBA_DONE) { + mgmt_frame->desc_word[4] = cpu_to_le16(ssn); + mgmt_frame->desc_word[7] = cpu_to_le16(tid | + (START_AMPDU_AGGR << 4) | + (RX_BA_INDICATION << 5) | + (peer_id << 8)); + } else if (event == STA_TX_DELBA) { + mgmt_frame->desc_word[7] = cpu_to_le16(tid | + (STOP_AMPDU_AGGR << 4) | + (peer_id << 8)); + } else if (event == STA_RX_DELBA) { + mgmt_frame->desc_word[7] = cpu_to_le16(tid | + (STOP_AMPDU_AGGR << 4) | + (RX_BA_INDICATION << 5) | + (peer_id << 8)); + } + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_program_bb_rf() - This function starts base band and RF programming. + * This is called after initial configurations are done. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +static int rsi_program_bb_rf(struct rsi_common *common) +{ + struct sk_buff *skb; + struct rsi_mac_frame *mgmt_frame; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending program BB/RF frame\n", __func__); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + mgmt_frame = (struct rsi_mac_frame *)skb->data; + + mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA); + mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8); + + if (common->rf_reset) { + mgmt_frame->desc_word[7] = cpu_to_le16(RF_RESET_ENABLE); + rsi_dbg(MGMT_TX_ZONE, "%s: ===> RF RESET REQUEST SENT <===\n", + __func__); + common->rf_reset = 0; + } + common->bb_rf_prog_count = 1; + mgmt_frame->desc_word[7] |= cpu_to_le16(PUT_BBP_RESET | + BBP_REG_WRITE | (RSI_RF_TYPE << 4)); + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_set_vap_capabilities() - This function send vap capability to firmware. + * @common: Pointer to the driver private structure. + * @opmode: Operating mode of device. + * + * Return: 0 on success, corresponding negative error code on failure. + */ +int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode) +{ + struct sk_buff *skb = NULL; + struct rsi_vap_caps *vap_caps; + u16 vap_id = 0; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__); + + skb = dev_alloc_skb(sizeof(struct rsi_vap_caps)); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, sizeof(struct rsi_vap_caps)); + vap_caps = (struct rsi_vap_caps *)skb->data; + + vap_caps->desc_word[0] = cpu_to_le16((sizeof(struct rsi_vap_caps) - + FRAME_DESC_SZ) | + (RSI_WIFI_MGMT_Q << 12)); + vap_caps->desc_word[1] = cpu_to_le16(VAP_CAPABILITIES); + vap_caps->desc_word[4] = cpu_to_le16(mode | + (common->channel_width << 8)); + vap_caps->desc_word[7] = cpu_to_le16((vap_id << 8) | + (common->mac_id << 4) | + common->radio_id); + + memcpy(vap_caps->mac_addr, common->mac_addr, IEEE80211_ADDR_LEN); + vap_caps->keep_alive_period = cpu_to_le16(90); + vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD); + + vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold); + vap_caps->default_mgmt_rate = 0; + if (conf_is_ht40(&common->priv->hw->conf)) { + vap_caps->default_ctrl_rate = + cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16); + } else { + vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6); + } + vap_caps->default_data_rate = 0; + vap_caps->beacon_interval = cpu_to_le16(200); + vap_caps->dtim_period = cpu_to_le16(4); + + skb_put(skb, sizeof(*vap_caps)); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_hal_load_key() - This function is used to load keys within the firmware. + * @common: Pointer to the driver private structure. + * @data: Pointer to the key data. + * @key_len: Key length to be loaded. + * @key_type: Type of key: GROUP/PAIRWISE. + * @key_id: Key index. + * @cipher: Type of cipher used. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_hal_load_key(struct rsi_common *common, + u8 *data, + u16 key_len, + u8 key_type, + u8 key_id, + u32 cipher) +{ + struct sk_buff *skb = NULL; + struct rsi_set_key *set_key; + u16 key_descriptor = 0; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending load key frame\n", __func__); + + skb = dev_alloc_skb(sizeof(struct rsi_set_key)); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, sizeof(struct rsi_set_key)); + set_key = (struct rsi_set_key *)skb->data; + + if ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104)) { + key_len += 1; + key_descriptor |= BIT(2); + if (key_len >= 13) + key_descriptor |= BIT(3); + } else if (cipher != KEY_TYPE_CLEAR) { + key_descriptor |= BIT(4); + if (key_type == RSI_PAIRWISE_KEY) + key_id = 0; + if (cipher == WLAN_CIPHER_SUITE_TKIP) + key_descriptor |= BIT(5); + } + key_descriptor |= (key_type | BIT(13) | (key_id << 14)); + + set_key->desc_word[0] = cpu_to_le16((sizeof(struct rsi_set_key) - + FRAME_DESC_SZ) | + (RSI_WIFI_MGMT_Q << 12)); + set_key->desc_word[1] = cpu_to_le16(SET_KEY_REQ); + set_key->desc_word[4] = cpu_to_le16(key_descriptor); + + if ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104)) { + memcpy(&set_key->key[key_id][1], + data, + key_len * 2); + } else { + memcpy(&set_key->key[0][0], data, key_len); + } + + memcpy(set_key->tx_mic_key, &data[16], 8); + memcpy(set_key->rx_mic_key, &data[24], 8); + + skb_put(skb, sizeof(struct rsi_set_key)); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/* + * rsi_load_bootup_params() - This function send bootup params to the firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +static u8 rsi_load_bootup_params(struct rsi_common *common) +{ + struct sk_buff *skb; + struct rsi_boot_params *boot_params; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending boot params frame\n", __func__); + skb = dev_alloc_skb(sizeof(struct rsi_boot_params)); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, sizeof(struct rsi_boot_params)); + boot_params = (struct rsi_boot_params *)skb->data; + + rsi_dbg(MGMT_TX_ZONE, "%s:\n", __func__); + + if (common->channel_width == BW_40MHZ) { + memcpy(&boot_params->bootup_params, + &boot_params_40, + sizeof(struct bootup_params)); + rsi_dbg(MGMT_TX_ZONE, "%s: Packet 40MHZ <=== %d\n", __func__, + UMAC_CLK_40BW); + boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40BW); + } else { + memcpy(&boot_params->bootup_params, + &boot_params_20, + sizeof(struct bootup_params)); + if (boot_params_20.valid != cpu_to_le32(VALID_20)) { + boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_20BW); + rsi_dbg(MGMT_TX_ZONE, + "%s: Packet 20MHZ <=== %d\n", __func__, + UMAC_CLK_20BW); + } else { + boot_params->desc_word[7] = cpu_to_le16(UMAC_CLK_40MHZ); + rsi_dbg(MGMT_TX_ZONE, + "%s: Packet 20MHZ <=== %d\n", __func__, + UMAC_CLK_40MHZ); + } + } + + /** + * Bit{0:11} indicates length of the Packet + * Bit{12:15} indicates host queue number + */ + boot_params->desc_word[0] = cpu_to_le16(sizeof(struct bootup_params) | + (RSI_WIFI_MGMT_Q << 12)); + boot_params->desc_word[1] = cpu_to_le16(BOOTUP_PARAMS_REQUEST); + + skb_put(skb, sizeof(struct rsi_boot_params)); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_send_reset_mac() - This function prepares reset MAC request and sends an + * internal management frame to indicate it to firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +static int rsi_send_reset_mac(struct rsi_common *common) +{ + struct sk_buff *skb; + struct rsi_mac_frame *mgmt_frame; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending reset MAC frame\n", __func__); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + mgmt_frame = (struct rsi_mac_frame *)skb->data; + + mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + mgmt_frame->desc_word[1] = cpu_to_le16(RESET_MAC_REQ); + mgmt_frame->desc_word[4] = cpu_to_le16(RETRY_COUNT << 8); + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_set_channel() - This function programs the channel. + * @common: Pointer to the driver private structure. + * @channel: Channel value to be set. + * + * Return: 0 on success, corresponding error code on failure. + */ +int rsi_set_channel(struct rsi_common *common, u16 channel) +{ + struct sk_buff *skb = NULL; + struct rsi_mac_frame *mgmt_frame; + + rsi_dbg(MGMT_TX_ZONE, + "%s: Sending scan req frame\n", __func__); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + mgmt_frame = (struct rsi_mac_frame *)skb->data; + + if (common->band == IEEE80211_BAND_5GHZ) { + if ((channel >= 36) && (channel <= 64)) + channel = ((channel - 32) / 4); + else if ((channel > 64) && (channel <= 140)) + channel = ((channel - 102) / 4) + 8; + else if (channel >= 149) + channel = ((channel - 151) / 4) + 18; + else + return -EINVAL; + } else { + if (channel > 14) { + rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n", + __func__, channel, common->band); + return -EINVAL; + } + } + + mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + mgmt_frame->desc_word[1] = cpu_to_le16(SCAN_REQUEST); + mgmt_frame->desc_word[4] = cpu_to_le16(channel); + + mgmt_frame->desc_word[7] = cpu_to_le16(PUT_BBP_RESET | + BBP_REG_WRITE | + (RSI_RF_TYPE << 4)); + + mgmt_frame->desc_word[5] = cpu_to_le16(0x01); + + if (common->channel_width == BW_40MHZ) + mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8); + + common->channel = channel; + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_compare() - This function is used to compare two integers + * @a: pointer to the first integer + * @b: pointer to the second integer + * + * Return: 0 if both are equal, -1 if the first is smaller, else 1 + */ +static int rsi_compare(const void *a, const void *b) +{ + u16 _a = *(const u16 *)(a); + u16 _b = *(const u16 *)(b); + + if (_a > _b) + return -1; + + if (_a < _b) + return 1; + + return 0; +} + +/** + * rsi_map_rates() - This function is used to map selected rates to hw rates. + * @rate: The standard rate to be mapped. + * @offset: Offset that will be returned. + * + * Return: 0 if it is a mcs rate, else 1 + */ +static bool rsi_map_rates(u16 rate, int *offset) +{ + int kk; + for (kk = 0; kk < ARRAY_SIZE(rsi_mcsrates); kk++) { + if (rate == mcs[kk]) { + *offset = kk; + return false; + } + } + + for (kk = 0; kk < ARRAY_SIZE(rsi_rates); kk++) { + if (rate == rsi_rates[kk].bitrate / 5) { + *offset = kk; + break; + } + } + return true; +} + +/** + * rsi_send_auto_rate_request() - This function is to set rates for connection + * and send autorate request to firmware. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, corresponding error code on failure. + */ +static int rsi_send_auto_rate_request(struct rsi_common *common) +{ + struct sk_buff *skb; + struct rsi_auto_rate *auto_rate; + int ii = 0, jj = 0, kk = 0; + struct ieee80211_hw *hw = common->priv->hw; + u8 band = hw->conf.chandef.chan->band; + u8 num_supported_rates = 0; + u8 rate_offset = 0; + u32 rate_bitmap = common->bitrate_mask[band]; + + u16 *selected_rates, min_rate; + + skb = dev_alloc_skb(sizeof(struct rsi_auto_rate)); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL); + if (!selected_rates) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, sizeof(struct rsi_auto_rate)); + memset(selected_rates, 0, 2 * RSI_TBL_SZ); + + auto_rate = (struct rsi_auto_rate *)skb->data; + + auto_rate->aarf_rssi = cpu_to_le16(((u16)3 << 6) | (u16)(18 & 0x3f)); + auto_rate->collision_tolerance = cpu_to_le16(3); + auto_rate->failure_limit = cpu_to_le16(3); + auto_rate->initial_boundary = cpu_to_le16(3); + auto_rate->max_threshold_limt = cpu_to_le16(27); + + auto_rate->desc_word[1] = cpu_to_le16(AUTO_RATE_IND); + + if (common->channel_width == BW_40MHZ) + auto_rate->desc_word[7] |= cpu_to_le16(1); + + if (band == IEEE80211_BAND_2GHZ) + min_rate = STD_RATE_01; + else + min_rate = STD_RATE_06; + + for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) { + if (rate_bitmap & BIT(ii)) { + selected_rates[jj++] = (rsi_rates[ii].bitrate / 5); + rate_offset++; + } + } + num_supported_rates = jj; + + if (common->vif_info[0].is_ht) { + for (ii = 0; ii < ARRAY_SIZE(mcs); ii++) + selected_rates[jj++] = mcs[ii]; + num_supported_rates += ARRAY_SIZE(mcs); + rate_offset += ARRAY_SIZE(mcs); + } + + if (rate_offset < (RSI_TBL_SZ / 2) - 1) { + for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) { + selected_rates[jj++] = min_rate; + rate_offset++; + } + } + + sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL); + + /* mapping the rates to RSI rates */ + for (ii = 0; ii < jj; ii++) { + if (rsi_map_rates(selected_rates[ii], &kk)) { + auto_rate->supported_rates[ii] = + cpu_to_le16(rsi_rates[kk].hw_value); + } else { + auto_rate->supported_rates[ii] = + cpu_to_le16(rsi_mcsrates[kk]); + } + } + + /* loading HT rates in the bottom half of the auto rate table */ + if (common->vif_info[0].is_ht) { + if (common->vif_info[0].sgi) + auto_rate->supported_rates[rate_offset++] = + cpu_to_le16(RSI_RATE_MCS7_SG); + + for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1; + ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) { + if (common->vif_info[0].sgi) + auto_rate->supported_rates[ii++] = + cpu_to_le16(rsi_mcsrates[kk] | BIT(9)); + auto_rate->supported_rates[ii] = + cpu_to_le16(rsi_mcsrates[kk--]); + } + + for (; ii < RSI_TBL_SZ; ii++) { + auto_rate->supported_rates[ii] = + cpu_to_le16(rsi_mcsrates[0]); + } + } + + auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2); + auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2); + auto_rate->desc_word[7] |= cpu_to_le16(0 << 8); + num_supported_rates *= 2; + + auto_rate->desc_word[0] = cpu_to_le16((sizeof(*auto_rate) - + FRAME_DESC_SZ) | + (RSI_WIFI_MGMT_Q << 12)); + + skb_put(skb, + sizeof(struct rsi_auto_rate)); + kfree(selected_rates); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_inform_bss_status() - This function informs about bss status with the + * help of sta notify params by sending an internal + * management frame to firmware. + * @common: Pointer to the driver private structure. + * @status: Bss status type. + * @bssid: Bssid. + * @qos_enable: Qos is enabled. + * @aid: Aid (unique for all STAs). + * + * Return: None. + */ +void rsi_inform_bss_status(struct rsi_common *common, + u8 status, + const unsigned char *bssid, + u8 qos_enable, + u16 aid) +{ + if (status) { + rsi_hal_send_sta_notify_frame(common, + NL80211_IFTYPE_STATION, + STA_CONNECTED, + bssid, + qos_enable, + aid); + if (common->min_rate == 0xffff) + rsi_send_auto_rate_request(common); + } else { + rsi_hal_send_sta_notify_frame(common, + NL80211_IFTYPE_STATION, + STA_DISCONNECTED, + bssid, + qos_enable, + aid); + } +} + +/** + * rsi_eeprom_read() - This function sends a frame to read the mac address + * from the eeprom. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_eeprom_read(struct rsi_common *common) +{ + struct rsi_mac_frame *mgmt_frame; + struct sk_buff *skb; + + rsi_dbg(MGMT_TX_ZONE, "%s: Sending EEPROM read req frame\n", __func__); + + skb = dev_alloc_skb(FRAME_DESC_SZ); + if (!skb) { + rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n", + __func__); + return -ENOMEM; + } + + memset(skb->data, 0, FRAME_DESC_SZ); + mgmt_frame = (struct rsi_mac_frame *)skb->data; + + /* FrameType */ + mgmt_frame->desc_word[1] = cpu_to_le16(EEPROM_READ_TYPE); + mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); + /* Number of bytes to read */ + mgmt_frame->desc_word[3] = cpu_to_le16(ETH_ALEN + + WLAN_MAC_MAGIC_WORD_LEN + + WLAN_HOST_MODE_LEN + + WLAN_FW_VERSION_LEN); + /* Address to read */ + mgmt_frame->desc_word[4] = cpu_to_le16(WLAN_MAC_EEPROM_ADDR); + + skb_put(skb, FRAME_DESC_SZ); + + return rsi_send_internal_mgmt_frame(common, skb); +} + +/** + * rsi_handle_ta_confirm_type() - This function handles the confirm frames. + * @common: Pointer to the driver private structure. + * @msg: Pointer to received packet. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_handle_ta_confirm_type(struct rsi_common *common, + u8 *msg) +{ + u8 sub_type = (msg[15] & 0xff); + + switch (sub_type) { + case BOOTUP_PARAMS_REQUEST: + rsi_dbg(FSM_ZONE, "%s: Boot up params confirm received\n", + __func__); + if (common->fsm_state == FSM_BOOT_PARAMS_SENT) { + if (rsi_eeprom_read(common)) { + common->fsm_state = FSM_CARD_NOT_READY; + goto out; + } else { + common->fsm_state = FSM_EEPROM_READ_MAC_ADDR; + } + } else { + rsi_dbg(ERR_ZONE, + "%s: Received bootup params cfm in %d state\n", + __func__, common->fsm_state); + return 0; + } + break; + + case EEPROM_READ_TYPE: + if (common->fsm_state == FSM_EEPROM_READ_MAC_ADDR) { + if (msg[16] == MAGIC_WORD) { + u8 offset = (FRAME_DESC_SZ + WLAN_HOST_MODE_LEN + + WLAN_MAC_MAGIC_WORD_LEN); + memcpy(common->mac_addr, + &msg[offset], + ETH_ALEN); + memcpy(&common->fw_ver, + &msg[offset + ETH_ALEN], + sizeof(struct version_info)); + + } else { + common->fsm_state = FSM_CARD_NOT_READY; + break; + } + if (rsi_send_reset_mac(common)) + goto out; + else + common->fsm_state = FSM_RESET_MAC_SENT; + } else { + rsi_dbg(ERR_ZONE, + "%s: Received eeprom mac addr in %d state\n", + __func__, common->fsm_state); + return 0; + } + break; + + case RESET_MAC_REQ: + if (common->fsm_state == FSM_RESET_MAC_SENT) { + rsi_dbg(FSM_ZONE, "%s: Reset MAC cfm received\n", + __func__); + + if (rsi_load_radio_caps(common)) + goto out; + else + common->fsm_state = FSM_RADIO_CAPS_SENT; + } else { + rsi_dbg(ERR_ZONE, + "%s: Received reset mac cfm in %d state\n", + __func__, common->fsm_state); + return 0; + } + break; + + case RADIO_CAPABILITIES: + if (common->fsm_state == FSM_RADIO_CAPS_SENT) { + common->rf_reset = 1; + if (rsi_program_bb_rf(common)) { + goto out; + } else { + common->fsm_state = FSM_BB_RF_PROG_SENT; + rsi_dbg(FSM_ZONE, "%s: Radio cap cfm received\n", + __func__); + } + } else { + rsi_dbg(ERR_ZONE, + "%s: Received radio caps cfm in %d state\n", + __func__, common->fsm_state); + return 0; + } + break; + + case BB_PROG_VALUES_REQUEST: + case RF_PROG_VALUES_REQUEST: + case BBP_PROG_IN_TA: + rsi_dbg(FSM_ZONE, "%s: BB/RF cfm received\n", __func__); + if (common->fsm_state == FSM_BB_RF_PROG_SENT) { + common->bb_rf_prog_count--; + if (!common->bb_rf_prog_count) { + common->fsm_state = FSM_MAC_INIT_DONE; + return rsi_mac80211_attach(common); + } + } else { + goto out; + } + break; + + default: + rsi_dbg(INFO_ZONE, "%s: Invalid TA confirm pkt received\n", + __func__); + break; + } + return 0; +out: + rsi_dbg(ERR_ZONE, "%s: Unable to send pkt/Invalid frame received\n", + __func__); + return -EINVAL; +} + +/** + * rsi_mgmt_pkt_recv() - This function processes the management packets + * recieved from the hardware. + * @common: Pointer to the driver private structure. + * @msg: Pointer to the received packet. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg) +{ + s32 msg_len = (le16_to_cpu(*(__le16 *)&msg[0]) & 0x0fff); + u16 msg_type = (msg[2]); + + rsi_dbg(FSM_ZONE, "%s: Msg Len: %d, Msg Type: %4x\n", + __func__, msg_len, msg_type); + + if (msg_type == TA_CONFIRM_TYPE) { + return rsi_handle_ta_confirm_type(common, msg); + } else if (msg_type == CARD_READY_IND) { + rsi_dbg(FSM_ZONE, "%s: Card ready indication received\n", + __func__); + if (common->fsm_state == FSM_CARD_NOT_READY) { + rsi_set_default_parameters(common); + + if (rsi_load_bootup_params(common)) + return -ENOMEM; + else + common->fsm_state = FSM_BOOT_PARAMS_SENT; + } else { + return -EINVAL; + } + } else if (msg_type == TX_STATUS_IND) { + if (msg[15] == PROBEREQ_CONFIRM) + common->mgmt_q_block = false; + rsi_dbg(FSM_ZONE, "%s: Probe confirm received\n", + __func__); + } else { + return rsi_mgmt_pkt_to_core(common, msg, msg_len, msg_type); + } + return 0; +} diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c new file mode 100644 index 00000000000..8e48e72bae2 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 "rsi_mgmt.h" + +/** + * rsi_send_data_pkt() - This function sends the recieved data packet from + * driver to device. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_hdr *tmp_hdr = NULL; + struct ieee80211_tx_info *info; + struct skb_info *tx_params; + struct ieee80211_bss_conf *bss = NULL; + int status = -EINVAL; + u8 ieee80211_size = MIN_802_11_HDR_LEN; + u8 extnd_size = 0; + __le16 *frame_desc; + u16 seq_num = 0; + + info = IEEE80211_SKB_CB(skb); + bss = &info->control.vif->bss_conf; + tx_params = (struct skb_info *)info->driver_data; + + if (!bss->assoc) + goto err; + + tmp_hdr = (struct ieee80211_hdr *)&skb->data[0]; + seq_num = (le16_to_cpu(tmp_hdr->seq_ctrl) >> 4); + + extnd_size = ((uintptr_t)skb->data & 0x3); + + if ((FRAME_DESC_SZ + extnd_size) > skb_headroom(skb)) { + rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); + status = -ENOSPC; + goto err; + } + + skb_push(skb, (FRAME_DESC_SZ + extnd_size)); + frame_desc = (__le16 *)&skb->data[0]; + memset((u8 *)frame_desc, 0, FRAME_DESC_SZ); + + if (ieee80211_is_data_qos(tmp_hdr->frame_control)) { + ieee80211_size += 2; + frame_desc[6] |= cpu_to_le16(BIT(12)); + } + + if ((!(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) && + (common->secinfo.security_enable)) { + if (rsi_is_cipher_wep(common)) + ieee80211_size += 4; + else + ieee80211_size += 8; + frame_desc[6] |= cpu_to_le16(BIT(15)); + } + + frame_desc[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) | + (RSI_WIFI_DATA_Q << 12)); + frame_desc[2] = cpu_to_le16((extnd_size) | (ieee80211_size) << 8); + + if (common->min_rate != 0xffff) { + /* Send fixed rate */ + frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE); + frame_desc[4] = cpu_to_le16(common->min_rate); + } + + frame_desc[6] |= cpu_to_le16(seq_num & 0xfff); + frame_desc[7] = cpu_to_le16(((tx_params->tid & 0xf) << 4) | + (skb->priority & 0xf) | + (tx_params->sta_id << 8)); + + status = adapter->host_intf_write_pkt(common->priv, + skb->data, + skb->len); + if (status) + rsi_dbg(ERR_ZONE, "%s: Failed to write pkt\n", + __func__); + +err: + ++common->tx_stats.total_tx_pkt_freed[skb->priority]; + rsi_indicate_tx_status(common->priv, skb, status); + return status; +} + +/** + * rsi_send_mgmt_pkt() - This functions sends the received management packet + * from driver to device. + * @common: Pointer to the driver private structure. + * @skb: Pointer to the socket buffer structure. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_send_mgmt_pkt(struct rsi_common *common, + struct sk_buff *skb) +{ + struct rsi_hw *adapter = common->priv; + struct ieee80211_hdr *wh = NULL; + struct ieee80211_tx_info *info; + struct ieee80211_bss_conf *bss = NULL; + struct skb_info *tx_params; + int status = -E2BIG; + __le16 *msg = NULL; + u8 extnd_size = 0; + u8 vap_id = 0; + + info = IEEE80211_SKB_CB(skb); + tx_params = (struct skb_info *)info->driver_data; + extnd_size = ((uintptr_t)skb->data & 0x3); + + if (tx_params->flags & INTERNAL_MGMT_PKT) { + if ((extnd_size) > skb_headroom(skb)) { + rsi_dbg(ERR_ZONE, "%s: Unable to send pkt\n", __func__); + dev_kfree_skb(skb); + return -ENOSPC; + } + skb_push(skb, extnd_size); + skb->data[extnd_size + 4] = extnd_size; + status = adapter->host_intf_write_pkt(common->priv, + (u8 *)skb->data, + skb->len); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to write the packet\n", __func__); + } + dev_kfree_skb(skb); + return status; + } + + bss = &info->control.vif->bss_conf; + wh = (struct ieee80211_hdr *)&skb->data[0]; + + if (FRAME_DESC_SZ > skb_headroom(skb)) + goto err; + + skb_push(skb, FRAME_DESC_SZ); + memset(skb->data, 0, FRAME_DESC_SZ); + msg = (__le16 *)skb->data; + + if (skb->len > MAX_MGMT_PKT_SIZE) { + rsi_dbg(INFO_ZONE, "%s: Dropping mgmt pkt > 512\n", __func__); + goto err; + } + + msg[0] = cpu_to_le16((skb->len - FRAME_DESC_SZ) | + (RSI_WIFI_MGMT_Q << 12)); + msg[1] = cpu_to_le16(TX_DOT11_MGMT); + msg[2] = cpu_to_le16(MIN_802_11_HDR_LEN << 8); + msg[3] = cpu_to_le16(RATE_INFO_ENABLE); + msg[6] = cpu_to_le16(le16_to_cpu(wh->seq_ctrl) >> 4); + + if (wh->addr1[0] & BIT(0)) + msg[3] |= cpu_to_le16(RSI_BROADCAST_PKT); + + if (common->band == IEEE80211_BAND_2GHZ) + msg[4] = cpu_to_le16(RSI_11B_MODE); + else + msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE); + + /* Indicate to firmware to give cfm */ + if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) { + msg[1] |= cpu_to_le16(BIT(10)); + msg[7] = cpu_to_le16(PROBEREQ_CONFIRM); + common->mgmt_q_block = true; + } + + msg[7] |= cpu_to_le16(vap_id << 8); + + status = adapter->host_intf_write_pkt(common->priv, + (u8 *)msg, + skb->len); + if (status) + rsi_dbg(ERR_ZONE, "%s: Failed to write the packet\n", __func__); + +err: + rsi_indicate_tx_status(common->priv, skb, status); + return status; +} diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c new file mode 100644 index 00000000000..852453f386e --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -0,0 +1,850 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 <linux/module.h> +#include "rsi_sdio.h" +#include "rsi_common.h" + +/** + * rsi_sdio_set_cmd52_arg() - This function prepares cmd 52 read/write arg. + * @rw: Read/write + * @func: function number + * @raw: indicates whether to perform read after write + * @address: address to which to read/write + * @writedata: data to write + * + * Return: argument + */ +static u32 rsi_sdio_set_cmd52_arg(bool rw, + u8 func, + u8 raw, + u32 address, + u8 writedata) +{ + return ((rw & 1) << 31) | ((func & 0x7) << 28) | + ((raw & 1) << 27) | (1 << 26) | + ((address & 0x1FFFF) << 9) | (1 << 8) | + (writedata & 0xFF); +} + +/** + * rsi_cmd52writebyte() - This function issues cmd52 byte write onto the card. + * @card: Pointer to the mmc_card. + * @address: Address to write. + * @byte: Data to write. + * + * Return: Write status. + */ +static int rsi_cmd52writebyte(struct mmc_card *card, + u32 address, + u8 byte) +{ + struct mmc_command io_cmd; + u32 arg; + + memset(&io_cmd, 0, sizeof(io_cmd)); + arg = rsi_sdio_set_cmd52_arg(1, 0, 0, address, byte); + io_cmd.opcode = SD_IO_RW_DIRECT; + io_cmd.arg = arg; + io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; + + return mmc_wait_for_cmd(card->host, &io_cmd, 0); +} + +/** + * rsi_cmd52readbyte() - This function issues cmd52 byte read onto the card. + * @card: Pointer to the mmc_card. + * @address: Address to read from. + * @byte: Variable to store read value. + * + * Return: Read status. + */ +static int rsi_cmd52readbyte(struct mmc_card *card, + u32 address, + u8 *byte) +{ + struct mmc_command io_cmd; + u32 arg; + int err; + + memset(&io_cmd, 0, sizeof(io_cmd)); + arg = rsi_sdio_set_cmd52_arg(0, 0, 0, address, 0); + io_cmd.opcode = SD_IO_RW_DIRECT; + io_cmd.arg = arg; + io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &io_cmd, 0); + if ((!err) && (byte)) + *byte = io_cmd.resp[0] & 0xFF; + return err; +} + +/** + * rsi_issue_sdiocommand() - This function issues sdio commands. + * @func: Pointer to the sdio_func structure. + * @opcode: Opcode value. + * @arg: Arguments to pass. + * @flags: Flags which are set. + * @resp: Pointer to store response. + * + * Return: err: command status as 0 or -1. + */ +static int rsi_issue_sdiocommand(struct sdio_func *func, + u32 opcode, + u32 arg, + u32 flags, + u32 *resp) +{ + struct mmc_command cmd; + struct mmc_host *host; + int err; + + host = func->card->host; + + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = opcode; + cmd.arg = arg; + cmd.flags = flags; + err = mmc_wait_for_cmd(host, &cmd, 3); + + if ((!err) && (resp)) + *resp = cmd.resp[0]; + + return err; +} + +/** + * rsi_handle_interrupt() - This function is called upon the occurence + * of an interrupt. + * @function: Pointer to the sdio_func structure. + * + * Return: None. + */ +static void rsi_handle_interrupt(struct sdio_func *function) +{ + struct rsi_hw *adapter = sdio_get_drvdata(function); + + sdio_release_host(function); + rsi_interrupt_handler(adapter); + sdio_claim_host(function); +} + +/** + * rsi_reset_card() - This function resets and re-initializes the card. + * @pfunction: Pointer to the sdio_func structure. + * + * Return: None. + */ +static void rsi_reset_card(struct sdio_func *pfunction) +{ + int ret = 0; + int err; + struct mmc_card *card = pfunction->card; + struct mmc_host *host = card->host; + s32 bit = (fls(host->ocr_avail) - 1); + u8 cmd52_resp; + u32 clock, resp, i; + u16 rca; + + /* Reset 9110 chip */ + ret = rsi_cmd52writebyte(pfunction->card, + SDIO_CCCR_ABORT, + (1 << 3)); + + /* Card will not send any response as it is getting reset immediately + * Hence expect a timeout status from host controller + */ + if (ret != -ETIMEDOUT) + rsi_dbg(ERR_ZONE, "%s: Reset failed : %d\n", __func__, ret); + + /* Wait for few milli seconds to get rid of residue charges if any */ + msleep(20); + + /* Initialize the SDIO card */ + host->ios.vdd = bit; + host->ios.chip_select = MMC_CS_DONTCARE; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.power_mode = MMC_POWER_UP; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + host->ops->set_ios(host, &host->ios); + + /* + * This delay should be sufficient to allow the power supply + * to reach the minimum voltage. + */ + msleep(20); + + host->ios.clock = host->f_min; + host->ios.power_mode = MMC_POWER_ON; + host->ops->set_ios(host, &host->ios); + + /* + * This delay must be at least 74 clock sizes, or 1 ms, or the + * time required to reach a stable voltage. + */ + msleep(20); + + /* Issue CMD0. Goto idle state */ + host->ios.chip_select = MMC_CS_HIGH; + host->ops->set_ios(host, &host->ios); + msleep(20); + err = rsi_issue_sdiocommand(pfunction, + MMC_GO_IDLE_STATE, + 0, + (MMC_RSP_NONE | MMC_CMD_BC), + NULL); + host->ios.chip_select = MMC_CS_DONTCARE; + host->ops->set_ios(host, &host->ios); + msleep(20); + host->use_spi_crc = 0; + + if (err) + rsi_dbg(ERR_ZONE, "%s: CMD0 failed : %d\n", __func__, err); + + if (!host->ocr_avail) { + /* Issue CMD5, arg = 0 */ + err = rsi_issue_sdiocommand(pfunction, + SD_IO_SEND_OP_COND, + 0, + (MMC_RSP_R4 | MMC_CMD_BCR), + &resp); + if (err) + rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", + __func__, err); + host->ocr_avail = resp; + } + + /* Issue CMD5, arg = ocr. Wait till card is ready */ + for (i = 0; i < 100; i++) { + err = rsi_issue_sdiocommand(pfunction, + SD_IO_SEND_OP_COND, + host->ocr_avail, + (MMC_RSP_R4 | MMC_CMD_BCR), + &resp); + if (err) { + rsi_dbg(ERR_ZONE, "%s: CMD5 failed : %d\n", + __func__, err); + break; + } + + if (resp & MMC_CARD_BUSY) + break; + msleep(20); + } + + if ((i == 100) || (err)) { + rsi_dbg(ERR_ZONE, "%s: card in not ready : %d %d\n", + __func__, i, err); + return; + } + + /* Issue CMD3, get RCA */ + err = rsi_issue_sdiocommand(pfunction, + SD_SEND_RELATIVE_ADDR, + 0, + (MMC_RSP_R6 | MMC_CMD_BCR), + &resp); + if (err) { + rsi_dbg(ERR_ZONE, "%s: CMD3 failed : %d\n", __func__, err); + return; + } + rca = resp >> 16; + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + host->ops->set_ios(host, &host->ios); + + /* Issue CMD7, select card */ + err = rsi_issue_sdiocommand(pfunction, + MMC_SELECT_CARD, + (rca << 16), + (MMC_RSP_R1 | MMC_CMD_AC), + NULL); + if (err) { + rsi_dbg(ERR_ZONE, "%s: CMD7 failed : %d\n", __func__, err); + return; + } + + /* Enable high speed */ + if (card->host->caps & MMC_CAP_SD_HIGHSPEED) { + rsi_dbg(ERR_ZONE, "%s: Set high speed mode\n", __func__); + err = rsi_cmd52readbyte(card, SDIO_CCCR_SPEED, &cmd52_resp); + if (err) { + rsi_dbg(ERR_ZONE, "%s: CCCR speed reg read failed: %d\n", + __func__, err); + card->state &= ~MMC_STATE_HIGHSPEED; + } else { + err = rsi_cmd52writebyte(card, + SDIO_CCCR_SPEED, + (cmd52_resp | SDIO_SPEED_EHS)); + if (err) { + rsi_dbg(ERR_ZONE, + "%s: CCR speed regwrite failed %d\n", + __func__, err); + return; + } + mmc_card_set_highspeed(card); + host->ios.timing = MMC_TIMING_SD_HS; + host->ops->set_ios(host, &host->ios); + } + } + + /* Set clock */ + if (mmc_card_highspeed(card)) + clock = 50000000; + else + clock = card->cis.max_dtr; + + if (clock > host->f_max) + clock = host->f_max; + + host->ios.clock = clock; + host->ops->set_ios(host, &host->ios); + + if (card->host->caps & MMC_CAP_4_BIT_DATA) { + /* CMD52: Set bus width & disable card detect resistor */ + err = rsi_cmd52writebyte(card, + SDIO_CCCR_IF, + (SDIO_BUS_CD_DISABLE | + SDIO_BUS_WIDTH_4BIT)); + if (err) { + rsi_dbg(ERR_ZONE, "%s: Set bus mode failed : %d\n", + __func__, err); + return; + } + host->ios.bus_width = MMC_BUS_WIDTH_4; + host->ops->set_ios(host, &host->ios); + } +} + +/** + * rsi_setclock() - This function sets the clock frequency. + * @adapter: Pointer to the adapter structure. + * @freq: Clock frequency. + * + * Return: None. + */ +static void rsi_setclock(struct rsi_hw *adapter, u32 freq) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + struct mmc_host *host = dev->pfunction->card->host; + u32 clock; + + clock = freq * 1000; + if (clock > host->f_max) + clock = host->f_max; + host->ios.clock = clock; + host->ops->set_ios(host, &host->ios); +} + +/** + * rsi_setblocklength() - This function sets the host block length. + * @adapter: Pointer to the adapter structure. + * @length: Block length to be set. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_setblocklength(struct rsi_hw *adapter, u32 length) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + int status; + rsi_dbg(INIT_ZONE, "%s: Setting the block length\n", __func__); + + status = sdio_set_block_size(dev->pfunction, length); + dev->pfunction->max_blksize = 256; + + rsi_dbg(INFO_ZONE, + "%s: Operational blk length is %d\n", __func__, length); + return status; +} + +/** + * rsi_setupcard() - This function queries and sets the card's features. + * @adapter: Pointer to the adapter structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_setupcard(struct rsi_hw *adapter) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + int status = 0; + + rsi_setclock(adapter, 50000); + + dev->tx_blk_size = 256; + status = rsi_setblocklength(adapter, dev->tx_blk_size); + if (status) + rsi_dbg(ERR_ZONE, + "%s: Unable to set block length\n", __func__); + return status; +} + +/** + * rsi_sdio_read_register() - This function reads one byte of information + * from a register. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that stores the data read. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_read_register(struct rsi_hw *adapter, + u32 addr, + u8 *data) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u8 fun_num = 0; + int status; + + sdio_claim_host(dev->pfunction); + + if (fun_num == 0) + *data = sdio_f0_readb(dev->pfunction, addr, &status); + else + *data = sdio_readb(dev->pfunction, addr, &status); + + sdio_release_host(dev->pfunction); + + return status; +} + +/** + * rsi_sdio_write_register() - This function writes one byte of information + * into a register. + * @adapter: Pointer to the adapter structure. + * @function: Function Number. + * @addr: Address of the register. + * @data: Pointer to the data tha has to be written. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_write_register(struct rsi_hw *adapter, + u8 function, + u32 addr, + u8 *data) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + int status = 0; + + sdio_claim_host(dev->pfunction); + + if (function == 0) + sdio_f0_writeb(dev->pfunction, *data, addr, &status); + else + sdio_writeb(dev->pfunction, *data, addr, &status); + + sdio_release_host(dev->pfunction); + + return status; +} + +/** + * rsi_sdio_ack_intr() - This function acks the interrupt received. + * @adapter: Pointer to the adapter structure. + * @int_bit: Interrupt bit to write into register. + * + * Return: None. + */ +void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit) +{ + int status; + status = rsi_sdio_write_register(adapter, + 1, + (SDIO_FUN1_INTR_CLR_REG | + RSI_SD_REQUEST_MASTER), + &int_bit); + if (status) + rsi_dbg(ERR_ZONE, "%s: unable to send ack\n", __func__); +} + + + +/** + * rsi_sdio_read_register_multiple() - This function read multiple bytes of + * information from the SD card. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @count: Number of multiple bytes to be read. + * @data: Pointer to the read data. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_sdio_read_register_multiple(struct rsi_hw *adapter, + u32 addr, + u32 count, + u8 *data) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u32 status; + + sdio_claim_host(dev->pfunction); + + status = sdio_readsb(dev->pfunction, data, addr, count); + + sdio_release_host(dev->pfunction); + + if (status != 0) + rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 read failed\n", __func__); + return status; +} + +/** + * rsi_sdio_write_register_multiple() - This function writes multiple bytes of + * information to the SD card. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that has to be written. + * @count: Number of multiple bytes to be written. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, + u32 addr, + u8 *data, + u32 count) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + int status; + + if (dev->write_fail > 1) { + rsi_dbg(ERR_ZONE, "%s: Stopping card writes\n", __func__); + return 0; + } else if (dev->write_fail == 1) { + /** + * Assuming it is a CRC failure, we want to allow another + * card write + */ + rsi_dbg(ERR_ZONE, "%s: Continue card writes\n", __func__); + dev->write_fail++; + } + + sdio_claim_host(dev->pfunction); + + status = sdio_writesb(dev->pfunction, addr, data, count); + + sdio_release_host(dev->pfunction); + + if (status) { + rsi_dbg(ERR_ZONE, "%s: Synch Cmd53 write failed %d\n", + __func__, status); + dev->write_fail = 2; + } else { + memcpy(dev->prev_desc, data, FRAME_DESC_SZ); + } + return status; +} + +/** + * rsi_sdio_host_intf_write_pkt() - This function writes the packet to device. + * @adapter: Pointer to the adapter structure. + * @pkt: Pointer to the data to be written on to the device. + * @len: length of the data to be written on to the device. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_sdio_host_intf_write_pkt(struct rsi_hw *adapter, + u8 *pkt, + u32 len) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u32 block_size = dev->tx_blk_size; + u32 num_blocks, address, length; + u32 queueno; + int status; + + queueno = ((pkt[1] >> 4) & 0xf); + + num_blocks = len / block_size; + + if (len % block_size) + num_blocks++; + + address = (num_blocks * block_size | (queueno << 12)); + length = num_blocks * block_size; + + status = rsi_sdio_write_register_multiple(adapter, + address, + (u8 *)pkt, + length); + if (status) + rsi_dbg(ERR_ZONE, "%s: Unable to write onto the card: %d\n", + __func__, status); + rsi_dbg(DATA_TX_ZONE, "%s: Successfully written onto card\n", __func__); + return status; +} + +/** + * rsi_sdio_host_intf_read_pkt() - This function reads the packet + from the device. + * @adapter: Pointer to the adapter data structure. + * @pkt: Pointer to the packet data to be read from the the device. + * @length: Length of the data to be read from the device. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter, + u8 *pkt, + u32 length) +{ + int status = -EINVAL; + + if (!length) { + rsi_dbg(ERR_ZONE, "%s: Pkt size is zero\n", __func__); + return status; + } + + status = rsi_sdio_read_register_multiple(adapter, + length, + length, /*num of bytes*/ + (u8 *)pkt); + + if (status) + rsi_dbg(ERR_ZONE, "%s: Failed to read frame: %d\n", __func__, + status); + return status; +} + +/** + * rsi_init_sdio_interface() - This function does init specific to SDIO. + * + * @adapter: Pointer to the adapter data structure. + * @pkt: Pointer to the packet data to be read from the the device. + * + * Return: 0 on success, -1 on failure. + */ + +static int rsi_init_sdio_interface(struct rsi_hw *adapter, + struct sdio_func *pfunction) +{ + struct rsi_91x_sdiodev *rsi_91x_dev; + int status = -ENOMEM; + + rsi_91x_dev = kzalloc(sizeof(*rsi_91x_dev), GFP_KERNEL); + if (!rsi_91x_dev) + return status; + + adapter->rsi_dev = rsi_91x_dev; + + sdio_claim_host(pfunction); + + pfunction->enable_timeout = 100; + status = sdio_enable_func(pfunction); + if (status) { + rsi_dbg(ERR_ZONE, "%s: Failed to enable interface\n", __func__); + sdio_release_host(pfunction); + return status; + } + + rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); + + rsi_91x_dev->pfunction = pfunction; + adapter->device = &pfunction->dev; + + sdio_set_drvdata(pfunction, adapter); + + status = rsi_setupcard(adapter); + if (status) { + rsi_dbg(ERR_ZONE, "%s: Failed to setup card\n", __func__); + goto fail; + } + + rsi_dbg(INIT_ZONE, "%s: Setup card succesfully\n", __func__); + + status = rsi_init_sdio_slave_regs(adapter); + if (status) { + rsi_dbg(ERR_ZONE, "%s: Failed to init slave regs\n", __func__); + goto fail; + } + sdio_release_host(pfunction); + + adapter->host_intf_write_pkt = rsi_sdio_host_intf_write_pkt; + adapter->host_intf_read_pkt = rsi_sdio_host_intf_read_pkt; + adapter->determine_event_timeout = rsi_sdio_determine_event_timeout; + adapter->check_hw_queue_status = rsi_sdio_read_buffer_status_register; + +#ifdef CONFIG_RSI_DEBUGFS + adapter->num_debugfs_entries = MAX_DEBUGFS_ENTRIES; +#endif + return status; +fail: + sdio_disable_func(pfunction); + sdio_release_host(pfunction); + return status; +} + +/** + * rsi_probe() - This function is called by kernel when the driver provided + * Vendor and device IDs are matched. All the initialization + * work is done here. + * @pfunction: Pointer to the sdio_func structure. + * @id: Pointer to sdio_device_id structure. + * + * Return: 0 on success, 1 on failure. + */ +static int rsi_probe(struct sdio_func *pfunction, + const struct sdio_device_id *id) +{ + struct rsi_hw *adapter; + + rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); + + adapter = rsi_91x_init(); + if (!adapter) { + rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", + __func__); + return 1; + } + + if (rsi_init_sdio_interface(adapter, pfunction)) { + rsi_dbg(ERR_ZONE, "%s: Failed to init sdio interface\n", + __func__); + goto fail; + } + + if (rsi_sdio_device_init(adapter->priv)) { + rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", __func__); + sdio_claim_host(pfunction); + sdio_disable_func(pfunction); + sdio_release_host(pfunction); + goto fail; + } + + sdio_claim_host(pfunction); + if (sdio_claim_irq(pfunction, rsi_handle_interrupt)) { + rsi_dbg(ERR_ZONE, "%s: Failed to request IRQ\n", __func__); + sdio_release_host(pfunction); + goto fail; + } + + sdio_release_host(pfunction); + rsi_dbg(INIT_ZONE, "%s: Registered Interrupt handler\n", __func__); + + return 0; +fail: + rsi_91x_deinit(adapter); + rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); + return 1; +} + +/** + * rsi_disconnect() - This function performs the reverse of the probe function. + * @pfunction: Pointer to the sdio_func structure. + * + * Return: void. + */ +static void rsi_disconnect(struct sdio_func *pfunction) +{ + struct rsi_hw *adapter = sdio_get_drvdata(pfunction); + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + + if (!adapter) + return; + + dev->write_fail = 2; + rsi_mac80211_detach(adapter); + + sdio_claim_host(pfunction); + sdio_release_irq(pfunction); + sdio_disable_func(pfunction); + rsi_91x_deinit(adapter); + /* Resetting to take care of the case, where-in driver is re-loaded */ + rsi_reset_card(pfunction); + sdio_release_host(pfunction); +} + +#ifdef CONFIG_PM +static int rsi_suspend(struct device *dev) +{ + /* Not yet implemented */ + return -ENOSYS; +} + +static int rsi_resume(struct device *dev) +{ + /* Not yet implemented */ + return -ENOSYS; +} + +static const struct dev_pm_ops rsi_pm_ops = { + .suspend = rsi_suspend, + .resume = rsi_resume, +}; +#endif + +static const struct sdio_device_id rsi_dev_table[] = { + { SDIO_DEVICE(0x303, 0x100) }, + { SDIO_DEVICE(0x041B, 0x0301) }, + { SDIO_DEVICE(0x041B, 0x0201) }, + { SDIO_DEVICE(0x041B, 0x9330) }, + { /* Blank */}, +}; + +static struct sdio_driver rsi_driver = { + .name = "RSI-SDIO WLAN", + .probe = rsi_probe, + .remove = rsi_disconnect, + .id_table = rsi_dev_table, +#ifdef CONFIG_PM + .drv = { + .pm = &rsi_pm_ops, + } +#endif +}; + +/** + * rsi_module_init() - This function registers the sdio module. + * @void: Void. + * + * Return: 0 on success. + */ +static int rsi_module_init(void) +{ + sdio_register_driver(&rsi_driver); + rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__); + return 0; +} + +/** + * rsi_module_exit() - This function unregisters the sdio module. + * @void: Void. + * + * Return: None. + */ +static void rsi_module_exit(void) +{ + sdio_unregister_driver(&rsi_driver); + rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__); +} + +module_init(rsi_module_init); +module_exit(rsi_module_exit); + +MODULE_AUTHOR("Redpine Signals Inc"); +MODULE_DESCRIPTION("Common SDIO layer for RSI drivers"); +MODULE_SUPPORTED_DEVICE("RSI-91x"); +MODULE_DEVICE_TABLE(sdio, rsi_dev_table); +MODULE_FIRMWARE(FIRMWARE_RSI9113); +MODULE_VERSION("0.1"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c new file mode 100644 index 00000000000..f1cb99cafed --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -0,0 +1,566 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 <linux/firmware.h> +#include "rsi_sdio.h" +#include "rsi_common.h" + +/** + * rsi_sdio_master_access_msword() - This function sets the AHB master access + * MS word in the SDIO slave registers. + * @adapter: Pointer to the adapter structure. + * @ms_word: ms word need to be initialized. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_sdio_master_access_msword(struct rsi_hw *adapter, + u16 ms_word) +{ + u8 byte; + u8 function = 0; + int status = 0; + + byte = (u8)(ms_word & 0x00FF); + + rsi_dbg(INIT_ZONE, + "%s: MASTER_ACCESS_MSBYTE:0x%x\n", __func__, byte); + + status = rsi_sdio_write_register(adapter, + function, + SDIO_MASTER_ACCESS_MSBYTE, + &byte); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: fail to access MASTER_ACCESS_MSBYTE\n", + __func__); + return -1; + } + + byte = (u8)(ms_word >> 8); + + rsi_dbg(INIT_ZONE, "%s:MASTER_ACCESS_LSBYTE:0x%x\n", __func__, byte); + status = rsi_sdio_write_register(adapter, + function, + SDIO_MASTER_ACCESS_LSBYTE, + &byte); + return status; +} + +/** + * rsi_copy_to_card() - This function includes the actual funtionality of + * copying the TA firmware to the card.Basically this + * function includes opening the TA file,reading the + * TA file and writing their values in blocks of data. + * @common: Pointer to the driver private structure. + * @fw: Pointer to the firmware value to be written. + * @len: length of firmware file. + * @num_blocks: Number of blocks to be written to the card. + * + * Return: 0 on success and -1 on failure. + */ +static int rsi_copy_to_card(struct rsi_common *common, + const u8 *fw, + u32 len, + u32 num_blocks) +{ + struct rsi_hw *adapter = common->priv; + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u32 indx, ii; + u32 block_size = dev->tx_blk_size; + u32 lsb_address; + __le32 data[] = { TA_HOLD_THREAD_VALUE, TA_SOFT_RST_CLR, + TA_PC_ZERO, TA_RELEASE_THREAD_VALUE }; + u32 address[] = { TA_HOLD_THREAD_REG, TA_SOFT_RESET_REG, + TA_TH0_PC_REG, TA_RELEASE_THREAD_REG }; + u32 base_address; + u16 msb_address; + + base_address = TA_LOAD_ADDRESS; + msb_address = base_address >> 16; + + for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) { + lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER); + if (rsi_sdio_write_register_multiple(adapter, + lsb_address, + (u8 *)(fw + indx), + block_size)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to load %s blk\n", __func__, + FIRMWARE_RSI9113); + return -1; + } + rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii); + base_address += block_size; + if ((base_address >> 16) != msb_address) { + msb_address += 1; + if (rsi_sdio_master_access_msword(adapter, + msb_address)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to set ms word reg\n", + __func__); + return -1; + } + } + } + + if (len % block_size) { + lsb_address = ((u16) base_address | RSI_SD_REQUEST_MASTER); + if (rsi_sdio_write_register_multiple(adapter, + lsb_address, + (u8 *)(fw + indx), + len % block_size)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to load f/w\n", __func__); + return -1; + } + } + rsi_dbg(INIT_ZONE, + "%s: Succesfully loaded TA instructions\n", __func__); + + if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to set ms word to common reg\n", + __func__); + return -1; + } + + for (ii = 0; ii < ARRAY_SIZE(data); ii++) { + /* Bringing TA out of reset */ + if (rsi_sdio_write_register_multiple(adapter, + (address[ii] | + RSI_SD_REQUEST_MASTER), + (u8 *)&data[ii], + 4)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to hold TA threads\n", __func__); + return -1; + } + } + + rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__); + return 0; +} + +/** + * rsi_load_ta_instructions() - This function includes the actual funtionality + * of loading the TA firmware.This function also + * includes opening the TA file,reading the TA + * file and writing their value in blocks of data. + * @common: Pointer to the driver private structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_load_ta_instructions(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u32 len; + u32 num_blocks; + const u8 *fw; + const struct firmware *fw_entry = NULL; + u32 block_size = dev->tx_blk_size; + int status = 0; + u32 base_address; + u16 msb_address; + + if (rsi_sdio_master_access_msword(adapter, TA_BASE_ADDR)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to set ms word to common reg\n", + __func__); + return -1; + } + base_address = TA_LOAD_ADDRESS; + msb_address = (base_address >> 16); + + if (rsi_sdio_master_access_msword(adapter, msb_address)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to set ms word reg\n", __func__); + return -1; + } + + status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device); + if (status < 0) { + rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n", + __func__, FIRMWARE_RSI9113); + return status; + } + + fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + len = fw_entry->size; + + if (len % 4) + len += (4 - (len % 4)); + + num_blocks = (len / block_size); + + rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); + rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); + + status = rsi_copy_to_card(common, fw, len, num_blocks); + release_firmware(fw_entry); + return status; +} + +/** + * rsi_process_pkt() - This Function reads rx_blocks register and figures out + * the size of the rx pkt. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_process_pkt(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + u8 num_blks = 0; + u32 rcv_pkt_len = 0; + int status = 0; + + status = rsi_sdio_read_register(adapter, + SDIO_RX_NUM_BLOCKS_REG, + &num_blks); + + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to read pkt length from the card:\n", + __func__); + return status; + } + rcv_pkt_len = (num_blks * 256); + + common->rx_data_pkt = kmalloc(rcv_pkt_len, GFP_KERNEL); + if (!common->rx_data_pkt) { + rsi_dbg(ERR_ZONE, "%s: Failed in memory allocation\n", + __func__); + return -1; + } + + status = rsi_sdio_host_intf_read_pkt(adapter, + common->rx_data_pkt, + rcv_pkt_len); + if (status) { + rsi_dbg(ERR_ZONE, "%s: Failed to read packet from card\n", + __func__); + goto fail; + } + + status = rsi_read_pkt(common, rcv_pkt_len); + kfree(common->rx_data_pkt); + return status; + +fail: + kfree(common->rx_data_pkt); + return -1; +} + +/** + * rsi_init_sdio_slave_regs() - This function does the actual initialization + * of SDBUS slave registers. + * @adapter: Pointer to the adapter structure. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_init_sdio_slave_regs(struct rsi_hw *adapter) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u8 function = 0; + u8 byte; + int status = 0; + + if (dev->next_read_delay) { + byte = dev->next_read_delay; + status = rsi_sdio_write_register(adapter, + function, + SDIO_NXT_RD_DELAY2, + &byte); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to write SDIO_NXT_RD_DELAY2\n", + __func__); + return -1; + } + } + + if (dev->sdio_high_speed_enable) { + rsi_dbg(INIT_ZONE, "%s: Enabling SDIO High speed\n", __func__); + byte = 0x3; + + status = rsi_sdio_write_register(adapter, + function, + SDIO_REG_HIGH_SPEED, + &byte); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to enable SDIO high speed\n", + __func__); + return -1; + } + } + + /* This tells SDIO FIFO when to start read to host */ + rsi_dbg(INIT_ZONE, "%s: Initialzing SDIO read start level\n", __func__); + byte = 0x24; + + status = rsi_sdio_write_register(adapter, + function, + SDIO_READ_START_LVL, + &byte); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to write SDIO_READ_START_LVL\n", __func__); + return -1; + } + + rsi_dbg(INIT_ZONE, "%s: Initialzing FIFO ctrl registers\n", __func__); + byte = (128 - 32); + + status = rsi_sdio_write_register(adapter, + function, + SDIO_READ_FIFO_CTL, + &byte); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to write SDIO_READ_FIFO_CTL\n", __func__); + return -1; + } + + byte = 32; + status = rsi_sdio_write_register(adapter, + function, + SDIO_WRITE_FIFO_CTL, + &byte); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to write SDIO_WRITE_FIFO_CTL\n", __func__); + return -1; + } + + return 0; +} + +/** + * rsi_interrupt_handler() - This function read and process SDIO interrupts. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +void rsi_interrupt_handler(struct rsi_hw *adapter) +{ + struct rsi_common *common = adapter->priv; + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + int status; + enum sdio_interrupt_type isr_type; + u8 isr_status = 0; + u8 fw_status = 0; + + dev->rx_info.sdio_int_counter++; + + do { + mutex_lock(&common->tx_rxlock); + status = rsi_sdio_read_register(common->priv, + RSI_FN1_INT_REGISTER, + &isr_status); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to Read Intr Status Register\n", + __func__); + mutex_unlock(&common->tx_rxlock); + return; + } + + if (isr_status == 0) { + rsi_set_event(&common->tx_thread.event); + dev->rx_info.sdio_intr_status_zero++; + mutex_unlock(&common->tx_rxlock); + return; + } + + rsi_dbg(ISR_ZONE, "%s: Intr_status = %x %d %d\n", + __func__, isr_status, (1 << MSDU_PKT_PENDING), + (1 << FW_ASSERT_IND)); + + do { + RSI_GET_SDIO_INTERRUPT_TYPE(isr_status, isr_type); + + switch (isr_type) { + case BUFFER_AVAILABLE: + dev->rx_info.watch_bufferfull_count = 0; + dev->rx_info.buffer_full = false; + dev->rx_info.mgmt_buffer_full = false; + rsi_sdio_ack_intr(common->priv, + (1 << PKT_BUFF_AVAILABLE)); + rsi_set_event((&common->tx_thread.event)); + rsi_dbg(ISR_ZONE, + "%s: ==> BUFFER_AVILABLE <==\n", + __func__); + dev->rx_info.buf_avilable_counter++; + break; + + case FIRMWARE_ASSERT_IND: + rsi_dbg(ERR_ZONE, + "%s: ==> FIRMWARE Assert <==\n", + __func__); + status = rsi_sdio_read_register(common->priv, + SDIO_FW_STATUS_REG, + &fw_status); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to read f/w reg\n", + __func__); + } else { + rsi_dbg(ERR_ZONE, + "%s: Firmware Status is 0x%x\n", + __func__ , fw_status); + rsi_sdio_ack_intr(common->priv, + (1 << FW_ASSERT_IND)); + } + + common->fsm_state = FSM_CARD_NOT_READY; + break; + + case MSDU_PACKET_PENDING: + rsi_dbg(ISR_ZONE, "Pkt pending interrupt\n"); + dev->rx_info.total_sdio_msdu_pending_intr++; + + status = rsi_process_pkt(common); + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to read pkt\n", + __func__); + mutex_unlock(&common->tx_rxlock); + return; + } + break; + default: + rsi_sdio_ack_intr(common->priv, isr_status); + dev->rx_info.total_sdio_unknown_intr++; + isr_status = 0; + rsi_dbg(ISR_ZONE, + "Unknown Interrupt %x\n", + isr_status); + break; + } + isr_status ^= BIT(isr_type - 1); + } while (isr_status); + mutex_unlock(&common->tx_rxlock); + } while (1); +} + +/** + * rsi_device_init() - This Function Initializes The HAL. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_sdio_device_init(struct rsi_common *common) +{ + if (rsi_load_ta_instructions(common)) + return -1; + + if (rsi_sdio_master_access_msword(common->priv, MISC_CFG_BASE_ADDR)) { + rsi_dbg(ERR_ZONE, "%s: Unable to set ms word reg\n", + __func__); + return -1; + } + rsi_dbg(INIT_ZONE, + "%s: Setting ms word to 0x41050000\n", __func__); + + return 0; +} + +/** + * rsi_sdio_read_buffer_status_register() - This function is used to the read + * buffer status register and set + * relevant fields in + * rsi_91x_sdiodev struct. + * @adapter: Pointer to the driver hw structure. + * @q_num: The Q number whose status is to be found. + * + * Return: status: -1 on failure or else queue full/stop is indicated. + */ +int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num) +{ + struct rsi_common *common = adapter->priv; + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + u8 buf_status = 0; + int status = 0; + + status = rsi_sdio_read_register(common->priv, + RSI_DEVICE_BUFFER_STATUS_REGISTER, + &buf_status); + + if (status) { + rsi_dbg(ERR_ZONE, + "%s: Failed to read status register\n", __func__); + return -1; + } + + if (buf_status & (BIT(PKT_MGMT_BUFF_FULL))) { + if (!dev->rx_info.mgmt_buffer_full) + dev->rx_info.mgmt_buf_full_counter++; + dev->rx_info.mgmt_buffer_full = true; + } else { + dev->rx_info.mgmt_buffer_full = false; + } + + if (buf_status & (BIT(PKT_BUFF_FULL))) { + if (!dev->rx_info.buffer_full) + dev->rx_info.buf_full_counter++; + dev->rx_info.buffer_full = true; + } else { + dev->rx_info.buffer_full = false; + } + + if (buf_status & (BIT(PKT_BUFF_SEMI_FULL))) { + if (!dev->rx_info.semi_buffer_full) + dev->rx_info.buf_semi_full_counter++; + dev->rx_info.semi_buffer_full = true; + } else { + dev->rx_info.semi_buffer_full = false; + } + + if ((q_num == MGMT_SOFT_Q) && (dev->rx_info.mgmt_buffer_full)) + return QUEUE_FULL; + + if (dev->rx_info.buffer_full) + return QUEUE_FULL; + + return QUEUE_NOT_FULL; +} + +/** + * rsi_sdio_determine_event_timeout() - This Function determines the event + * timeout duration. + * @adapter: Pointer to the adapter structure. + * + * Return: timeout duration is returned. + */ +int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter) +{ + struct rsi_91x_sdiodev *dev = + (struct rsi_91x_sdiodev *)adapter->rsi_dev; + + /* Once buffer full is seen, event timeout to occur every 2 msecs */ + if (dev->rx_info.buffer_full) + return 2; + + return EVENT_WAIT_FOREVER; +} diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c new file mode 100644 index 00000000000..bb1bf96670e --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -0,0 +1,575 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 <linux/module.h> +#include "rsi_usb.h" + +/** + * rsi_usb_card_write() - This function writes to the USB Card. + * @adapter: Pointer to the adapter structure. + * @buf: Pointer to the buffer from where the data has to be taken. + * @len: Length to be written. + * @endpoint: Type of endpoint. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_usb_card_write(struct rsi_hw *adapter, + void *buf, + u16 len, + u8 endpoint) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + int status; + s32 transfer; + + status = usb_bulk_msg(dev->usbdev, + usb_sndbulkpipe(dev->usbdev, + dev->bulkout_endpoint_addr[endpoint - 1]), + buf, + len, + &transfer, + HZ * 5); + + if (status < 0) { + rsi_dbg(ERR_ZONE, + "Card write failed with error code :%10d\n", status); + dev->write_fail = 1; + } + return status; +} + +/** + * rsi_write_multiple() - This function writes multiple bytes of information + * to the USB card. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that has to be written. + * @count: Number of multiple bytes to be written. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_write_multiple(struct rsi_hw *adapter, + u8 endpoint, + u8 *data, + u32 count) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + u8 *seg = dev->tx_buffer; + + if (dev->write_fail) + return 0; + + if (endpoint == MGMT_EP) { + memset(seg, 0, RSI_USB_TX_HEAD_ROOM); + memcpy(seg + RSI_USB_TX_HEAD_ROOM, data, count); + } else { + seg = ((u8 *)data - RSI_USB_TX_HEAD_ROOM); + } + + return rsi_usb_card_write(adapter, + seg, + count + RSI_USB_TX_HEAD_ROOM, + endpoint); +} + +/** + * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk + * endpoints to the device. + * @interface: Pointer to the USB interface structure. + * @adapter: Pointer to the adapter structure. + * + * Return: ret_val: 0 on success, -ENOMEM on failure. + */ +static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, + struct rsi_hw *adapter) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + __le16 buffer_size; + int ii, bep_found = 0; + + iface_desc = &(interface->altsetting[0]); + + for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { + endpoint = &(iface_desc->endpoint[ii].desc); + + if ((!(dev->bulkin_endpoint_addr)) && + (endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + buffer_size = endpoint->wMaxPacketSize; + dev->bulkin_size = buffer_size; + dev->bulkin_endpoint_addr = + endpoint->bEndpointAddress; + } + + if (!dev->bulkout_endpoint_addr[bep_found] && + !(endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + dev->bulkout_endpoint_addr[bep_found] = + endpoint->bEndpointAddress; + buffer_size = endpoint->wMaxPacketSize; + dev->bulkout_size[bep_found] = buffer_size; + bep_found++; + } + + if (bep_found >= MAX_BULK_EP) + break; + } + + if (!(dev->bulkin_endpoint_addr) && + (dev->bulkout_endpoint_addr[0])) + return -EINVAL; + + return 0; +} + +/* rsi_usb_reg_read() - This function reads data from given register address. + * @usbdev: Pointer to the usb_device structure. + * @reg: Address of the register to be read. + * @value: Value to be read. + * @len: length of data to be read. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_usb_reg_read(struct usb_device *usbdev, + u32 reg, + u16 *value, + u16 len) +{ + u8 temp_buf[4]; + int status = 0; + + status = usb_control_msg(usbdev, + usb_rcvctrlpipe(usbdev, 0), + USB_VENDOR_REGISTER_READ, + USB_TYPE_VENDOR, + ((reg & 0xffff0000) >> 16), (reg & 0xffff), + (void *)temp_buf, + len, + HZ * 5); + + *value = (temp_buf[0] | (temp_buf[1] << 8)); + if (status < 0) { + rsi_dbg(ERR_ZONE, + "%s: Reg read failed with error code :%d\n", + __func__, status); + } + return status; +} + +/** + * rsi_usb_reg_write() - This function writes the given data into the given + * register address. + * @usbdev: Pointer to the usb_device structure. + * @reg: Address of the register. + * @value: Value to write. + * @len: Length of data to be written. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_usb_reg_write(struct usb_device *usbdev, + u32 reg, + u16 value, + u16 len) +{ + u8 usb_reg_buf[4]; + int status = 0; + + usb_reg_buf[0] = (value & 0x00ff); + usb_reg_buf[1] = (value & 0xff00) >> 8; + usb_reg_buf[2] = 0x0; + usb_reg_buf[3] = 0x0; + + status = usb_control_msg(usbdev, + usb_sndctrlpipe(usbdev, 0), + USB_VENDOR_REGISTER_WRITE, + USB_TYPE_VENDOR, + ((reg & 0xffff0000) >> 16), + (reg & 0xffff), + (void *)usb_reg_buf, + len, + HZ * 5); + if (status < 0) { + rsi_dbg(ERR_ZONE, + "%s: Reg write failed with error code :%d\n", + __func__, status); + } + return status; +} + +/** + * rsi_rx_done_handler() - This function is called when a packet is received + * from USB stack. This is callback to recieve done. + * @urb: Received URB. + * + * Return: None. + */ +static void rsi_rx_done_handler(struct urb *urb) +{ + struct rsi_hw *adapter = urb->context; + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + + if (urb->status) + return; + + rsi_set_event(&dev->rx_thread.event); +} + +/** + * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. + * @adapter: Pointer to the adapter structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_rx_urb_submit(struct rsi_hw *adapter) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + struct urb *urb = dev->rx_usb_urb[0]; + int status; + + usb_fill_bulk_urb(urb, + dev->usbdev, + usb_rcvbulkpipe(dev->usbdev, + dev->bulkin_endpoint_addr), + urb->transfer_buffer, + 3000, + rsi_rx_done_handler, + adapter); + + status = usb_submit_urb(urb, GFP_KERNEL); + if (status) + rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); + + return status; +} + +/** + * rsi_usb_write_register_multiple() - This function writes multiple bytes of + * information to multiple registers. + * @adapter: Pointer to the adapter structure. + * @addr: Address of the register. + * @data: Pointer to the data that has to be written. + * @count: Number of multiple bytes to be written on to the registers. + * + * Return: status: 0 on success, -1 on failure. + */ +int rsi_usb_write_register_multiple(struct rsi_hw *adapter, + u32 addr, + u8 *data, + u32 count) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + u8 *buf; + u8 transfer; + int status = 0; + + buf = kzalloc(4096, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + while (count) { + transfer = min_t(int, count, 4096); + memcpy(buf, data, transfer); + status = usb_control_msg(dev->usbdev, + usb_sndctrlpipe(dev->usbdev, 0), + USB_VENDOR_REGISTER_WRITE, + USB_TYPE_VENDOR, + ((addr & 0xffff0000) >> 16), + (addr & 0xffff), + (void *)buf, + transfer, + HZ * 5); + if (status < 0) { + rsi_dbg(ERR_ZONE, + "Reg write failed with error code :%d\n", + status); + } else { + count -= transfer; + data += transfer; + addr += transfer; + } + } + + kfree(buf); + return 0; +} + +/** + *rsi_usb_host_intf_write_pkt() - This function writes the packet to the + * USB card. + * @adapter: Pointer to the adapter structure. + * @pkt: Pointer to the data to be written on to the card. + * @len: Length of the data to be written on to the card. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, + u8 *pkt, + u32 len) +{ + u32 queueno = ((pkt[1] >> 4) & 0xf); + u8 endpoint; + + endpoint = ((queueno == RSI_WIFI_MGMT_Q) ? MGMT_EP : DATA_EP); + + return rsi_write_multiple(adapter, + endpoint, + (u8 *)pkt, + len); +} + +/** + * rsi_deinit_usb_interface() - This function deinitializes the usb interface. + * @adapter: Pointer to the adapter structure. + * + * Return: None. + */ +static void rsi_deinit_usb_interface(struct rsi_hw *adapter) +{ + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + + rsi_kill_thread(&dev->rx_thread); + kfree(adapter->priv->rx_data_pkt); + kfree(dev->tx_buffer); +} + +/** + * rsi_init_usb_interface() - This function initializes the usb interface. + * @adapter: Pointer to the adapter structure. + * @pfunction: Pointer to USB interface structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_init_usb_interface(struct rsi_hw *adapter, + struct usb_interface *pfunction) +{ + struct rsi_91x_usbdev *rsi_dev; + struct rsi_common *common = adapter->priv; + int status; + + rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL); + if (!rsi_dev) + return -ENOMEM; + + adapter->rsi_dev = rsi_dev; + rsi_dev->usbdev = interface_to_usbdev(pfunction); + + if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) + return -EINVAL; + + adapter->device = &pfunction->dev; + usb_set_intfdata(pfunction, adapter); + + common->rx_data_pkt = kmalloc(2048, GFP_KERNEL); + if (!common->rx_data_pkt) { + rsi_dbg(ERR_ZONE, "%s: Failed to allocate memory\n", + __func__); + return -ENOMEM; + } + + rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC); + rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL); + rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt; + rsi_dev->tx_blk_size = 252; + + /* Initializing function callbacks */ + adapter->rx_urb_submit = rsi_rx_urb_submit; + adapter->host_intf_write_pkt = rsi_usb_host_intf_write_pkt; + adapter->check_hw_queue_status = rsi_usb_check_queue_status; + adapter->determine_event_timeout = rsi_usb_event_timeout; + + rsi_init_event(&rsi_dev->rx_thread.event); + status = rsi_create_kthread(common, &rsi_dev->rx_thread, + rsi_usb_rx_thread, "RX-Thread"); + if (status) { + rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__); + goto fail; + } + +#ifdef CONFIG_RSI_DEBUGFS + /* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */ + adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1); +#endif + + rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); + return 0; + +fail: + kfree(rsi_dev->tx_buffer); + kfree(common->rx_data_pkt); + return status; +} + +/** + * rsi_probe() - This function is called by kernel when the driver provided + * Vendor and device IDs are matched. All the initialization + * work is done here. + * @pfunction: Pointer to the USB interface structure. + * @id: Pointer to the usb_device_id structure. + * + * Return: 0 on success, -1 on failure. + */ +static int rsi_probe(struct usb_interface *pfunction, + const struct usb_device_id *id) +{ + struct rsi_hw *adapter; + struct rsi_91x_usbdev *dev; + u16 fw_status; + + rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); + + adapter = rsi_91x_init(); + if (!adapter) { + rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", + __func__); + return 1; + } + + if (rsi_init_usb_interface(adapter, pfunction)) { + rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n", + __func__); + goto err; + } + + rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__); + + dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + + if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0) + goto err1; + else + fw_status &= 1; + + if (!fw_status) { + if (rsi_usb_device_init(adapter->priv)) { + rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", + __func__); + goto err1; + } + + if (rsi_usb_reg_write(dev->usbdev, + USB_INTERNAL_REG_1, + RSI_USB_READY_MAGIC_NUM, 1) < 0) + goto err1; + rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__); + } + + if (rsi_rx_urb_submit(adapter)) + goto err1; + + return 0; +err1: + rsi_deinit_usb_interface(adapter); +err: + rsi_91x_deinit(adapter); + rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); + return 1; +} + +/** + * rsi_disconnect() - This function performs the reverse of the probe function, + * it deintialize the driver structure. + * @pfunction: Pointer to the USB interface structure. + * + * Return: None. + */ +static void rsi_disconnect(struct usb_interface *pfunction) +{ + struct rsi_hw *adapter = usb_get_intfdata(pfunction); + + if (!adapter) + return; + + rsi_mac80211_detach(adapter); + rsi_deinit_usb_interface(adapter); + rsi_91x_deinit(adapter); + + rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__); +} + +#ifdef CONFIG_PM +static int rsi_suspend(struct usb_interface *intf, pm_message_t message) +{ + /* Not yet implemented */ + return -ENOSYS; +} + +static int rsi_resume(struct usb_interface *intf) +{ + /* Not yet implemented */ + return -ENOSYS; +} +#endif + +static const struct usb_device_id rsi_dev_table[] = { + { USB_DEVICE(0x0303, 0x0100) }, + { USB_DEVICE(0x041B, 0x0301) }, + { USB_DEVICE(0x041B, 0x0201) }, + { USB_DEVICE(0x041B, 0x9330) }, + { /* Blank */}, +}; + +static struct usb_driver rsi_driver = { + .name = "RSI-USB WLAN", + .probe = rsi_probe, + .disconnect = rsi_disconnect, + .id_table = rsi_dev_table, +#ifdef CONFIG_PM + .suspend = rsi_suspend, + .resume = rsi_resume, +#endif +}; + +/** + * rsi_module_init() - This function registers the client driver. + * @void: Void. + * + * Return: 0 on success. + */ +static int rsi_module_init(void) +{ + usb_register(&rsi_driver); + rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__); + return 0; +} + +/** + * rsi_module_exit() - This function unregisters the client driver. + * @void: Void. + * + * Return: None. + */ +static void rsi_module_exit(void) +{ + usb_deregister(&rsi_driver); + rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__); +} + +module_init(rsi_module_init); +module_exit(rsi_module_exit); + +MODULE_AUTHOR("Redpine Signals Inc"); +MODULE_DESCRIPTION("Common USB layer for RSI drivers"); +MODULE_SUPPORTED_DEVICE("RSI-91x"); +MODULE_DEVICE_TABLE(usb, rsi_dev_table); +MODULE_FIRMWARE(FIRMWARE_RSI9113); +MODULE_VERSION("0.1"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c new file mode 100644 index 00000000000..1106ce76707 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 <linux/firmware.h> +#include "rsi_usb.h" + +/** + * rsi_copy_to_card() - This function includes the actual funtionality of + * copying the TA firmware to the card.Basically this + * function includes opening the TA file,reading the TA + * file and writing their values in blocks of data. + * @common: Pointer to the driver private structure. + * @fw: Pointer to the firmware value to be written. + * @len: length of firmware file. + * @num_blocks: Number of blocks to be written to the card. + * + * Return: 0 on success and -1 on failure. + */ +static int rsi_copy_to_card(struct rsi_common *common, + const u8 *fw, + u32 len, + u32 num_blocks) +{ + struct rsi_hw *adapter = common->priv; + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + u32 indx, ii; + u32 block_size = dev->tx_blk_size; + u32 lsb_address; + u32 base_address; + + base_address = TA_LOAD_ADDRESS; + + for (indx = 0, ii = 0; ii < num_blocks; ii++, indx += block_size) { + lsb_address = base_address; + if (rsi_usb_write_register_multiple(adapter, + lsb_address, + (u8 *)(fw + indx), + block_size)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to load %s blk\n", __func__, + FIRMWARE_RSI9113); + return -EIO; + } + rsi_dbg(INIT_ZONE, "%s: loading block: %d\n", __func__, ii); + base_address += block_size; + } + + if (len % block_size) { + lsb_address = base_address; + if (rsi_usb_write_register_multiple(adapter, + lsb_address, + (u8 *)(fw + indx), + len % block_size)) { + rsi_dbg(ERR_ZONE, + "%s: Unable to load %s blk\n", __func__, + FIRMWARE_RSI9113); + return -EIO; + } + } + rsi_dbg(INIT_ZONE, + "%s: Succesfully loaded %s instructions\n", __func__, + FIRMWARE_RSI9113); + + rsi_dbg(INIT_ZONE, "%s: loaded firmware\n", __func__); + return 0; +} + +/** + * rsi_usb_rx_thread() - This is a kernel thread to receive the packets from + * the USB device. + * @common: Pointer to the driver private structure. + * + * Return: None. + */ +void rsi_usb_rx_thread(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + int status; + + do { + rsi_wait_event(&dev->rx_thread.event, EVENT_WAIT_FOREVER); + + if (atomic_read(&dev->rx_thread.thread_done)) + goto out; + + mutex_lock(&common->tx_rxlock); + status = rsi_read_pkt(common, 0); + if (status) { + rsi_dbg(ERR_ZONE, "%s: Failed To read data", __func__); + mutex_unlock(&common->tx_rxlock); + return; + } + mutex_unlock(&common->tx_rxlock); + rsi_reset_event(&dev->rx_thread.event); + if (adapter->rx_urb_submit(adapter)) { + rsi_dbg(ERR_ZONE, + "%s: Failed in urb submission", __func__); + return; + } + } while (1); + +out: + rsi_dbg(INFO_ZONE, "%s: Terminated thread\n", __func__); + complete_and_exit(&dev->rx_thread.completion, 0); +} + + +/** + * rsi_load_ta_instructions() - This function includes the actual funtionality + * of loading the TA firmware.This function also + * includes opening the TA file,reading the TA + * file and writing their value in blocks of data. + * @common: Pointer to the driver private structure. + * + * Return: status: 0 on success, -1 on failure. + */ +static int rsi_load_ta_instructions(struct rsi_common *common) +{ + struct rsi_hw *adapter = common->priv; + struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; + const struct firmware *fw_entry = NULL; + u32 block_size = dev->tx_blk_size; + const u8 *fw; + u32 num_blocks, len; + int status = 0; + + status = request_firmware(&fw_entry, FIRMWARE_RSI9113, adapter->device); + if (status < 0) { + rsi_dbg(ERR_ZONE, "%s Firmware file %s not found\n", + __func__, FIRMWARE_RSI9113); + return status; + } + + fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + len = fw_entry->size; + + if (len % 4) + len += (4 - (len % 4)); + + num_blocks = (len / block_size); + + rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); + rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); + + status = rsi_copy_to_card(common, fw, len, num_blocks); + release_firmware(fw_entry); + return status; +} + +/** + * rsi_device_init() - This Function Initializes The HAL. + * @common: Pointer to the driver private structure. + * + * Return: 0 on success, -1 on failure. + */ +int rsi_usb_device_init(struct rsi_common *common) +{ + if (rsi_load_ta_instructions(common)) + return -EIO; + + return 0; + } diff --git a/drivers/net/wireless/rsi/rsi_boot_params.h b/drivers/net/wireless/rsi/rsi_boot_params.h new file mode 100644 index 00000000000..5e2721f7909 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_boot_params.h @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 __RSI_BOOTPARAMS_HEADER_H__ +#define __RSI_BOOTPARAMS_HEADER_H__ + +#define CRYSTAL_GOOD_TIME BIT(0) +#define BOOTUP_MODE_INFO BIT(1) +#define WIFI_TAPLL_CONFIGS BIT(5) +#define WIFI_PLL960_CONFIGS BIT(6) +#define WIFI_AFEPLL_CONFIGS BIT(7) +#define WIFI_SWITCH_CLK_CONFIGS BIT(8) + +#define TA_PLL_M_VAL_20 8 +#define TA_PLL_N_VAL_20 1 +#define TA_PLL_P_VAL_20 4 + +#define PLL960_M_VAL_20 0x14 +#define PLL960_N_VAL_20 0 +#define PLL960_P_VAL_20 5 + +#define UMAC_CLK_40MHZ 40 + +#define TA_PLL_M_VAL_40 46 +#define TA_PLL_N_VAL_40 3 +#define TA_PLL_P_VAL_40 3 + +#define PLL960_M_VAL_40 0x14 +#define PLL960_N_VAL_40 0 +#define PLL960_P_VAL_40 5 + +#define UMAC_CLK_20BW \ + (((TA_PLL_M_VAL_20 + 1) * 40) / \ + ((TA_PLL_N_VAL_20 + 1) * (TA_PLL_P_VAL_20 + 1))) +#define VALID_20 \ + (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS) +#define UMAC_CLK_40BW \ + (((TA_PLL_M_VAL_40 + 1) * 40) / \ + ((TA_PLL_N_VAL_40 + 1) * (TA_PLL_P_VAL_40 + 1))) +#define VALID_40 \ + (WIFI_PLL960_CONFIGS | WIFI_AFEPLL_CONFIGS | WIFI_SWITCH_CLK_CONFIGS | \ + WIFI_TAPLL_CONFIGS | CRYSTAL_GOOD_TIME | BOOTUP_MODE_INFO) + +/* structure to store configs related to TAPLL programming */ +struct tapll_info { + __le16 pll_reg_1; + __le16 pll_reg_2; +} __packed; + +/* structure to store configs related to PLL960 programming */ +struct pll960_info { + __le16 pll_reg_1; + __le16 pll_reg_2; + __le16 pll_reg_3; +} __packed; + +/* structure to store configs related to AFEPLL programming */ +struct afepll_info { + __le16 pll_reg; +} __packed; + +/* structure to store configs related to pll configs */ +struct pll_config { + struct tapll_info tapll_info_g; + struct pll960_info pll960_info_g; + struct afepll_info afepll_info_g; +} __packed; + +/* structure to store configs related to UMAC clk programming */ +struct switch_clk { + __le16 switch_clk_info; + /* If switch_bbp_lmac_clk_reg is set then this value will be programmed + * into reg + */ + __le16 bbp_lmac_clk_reg_val; + /* if switch_umac_clk is set then this value will be programmed */ + __le16 umac_clock_reg_config; + /* if switch_qspi_clk is set then this value will be programmed */ + __le16 qspi_uart_clock_reg_config; +} __packed; + +struct device_clk_info { + struct pll_config pll_config_g; + struct switch_clk switch_clk_g; +} __packed; + +struct bootup_params { + __le16 magic_number; + __le16 crystal_good_time; + __le32 valid; + __le32 reserved_for_valids; + __le16 bootup_mode_info; + /* configuration used for digital loop back */ + __le16 digital_loop_back_params; + __le16 rtls_timestamp_en; + __le16 host_spi_intr_cfg; + struct device_clk_info device_clk_info[3]; + /* ulp buckboost wait time */ + __le32 buckboost_wakeup_cnt; + /* pmu wakeup wait time & WDT EN info */ + __le16 pmu_wakeup_wait; + u8 shutdown_wait_time; + /* Sleep clock source selection */ + u8 pmu_slp_clkout_sel; + /* WDT programming values */ + __le32 wdt_prog_value; + /* WDT soc reset delay */ + __le32 wdt_soc_rst_delay; + /* dcdc modes configs */ + __le32 dcdc_operation_mode; + __le32 soc_reset_wait_cnt; +} __packed; +#endif diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h new file mode 100644 index 00000000000..f2f70784d4a --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 __RSI_COMMON_H__ +#define __RSI_COMMON_H__ + +#include <linux/kthread.h> + +#define EVENT_WAIT_FOREVER 0 +#define TA_LOAD_ADDRESS 0x00 +#define FIRMWARE_RSI9113 "rsi_91x.fw" +#define QUEUE_NOT_FULL 1 +#define QUEUE_FULL 0 + +static inline int rsi_init_event(struct rsi_event *pevent) +{ + atomic_set(&pevent->event_condition, 1); + init_waitqueue_head(&pevent->event_queue); + return 0; +} + +static inline int rsi_wait_event(struct rsi_event *event, u32 timeout) +{ + int status = 0; + + if (!timeout) + status = wait_event_interruptible(event->event_queue, + (atomic_read(&event->event_condition) == 0)); + else + status = wait_event_interruptible_timeout(event->event_queue, + (atomic_read(&event->event_condition) == 0), + timeout); + return status; +} + +static inline void rsi_set_event(struct rsi_event *event) +{ + atomic_set(&event->event_condition, 0); + wake_up_interruptible(&event->event_queue); +} + +static inline void rsi_reset_event(struct rsi_event *event) +{ + atomic_set(&event->event_condition, 1); +} + +static inline int rsi_create_kthread(struct rsi_common *common, + struct rsi_thread *thread, + void *func_ptr, + u8 *name) +{ + init_completion(&thread->completion); + thread->task = kthread_run(func_ptr, common, name); + if (IS_ERR(thread->task)) + return (int)PTR_ERR(thread->task); + + return 0; +} + +static inline int rsi_kill_thread(struct rsi_thread *handle) +{ + atomic_inc(&handle->thread_done); + rsi_set_event(&handle->event); + + wait_for_completion(&handle->completion); + return kthread_stop(handle->task); +} + +void rsi_mac80211_detach(struct rsi_hw *hw); +u16 rsi_get_connected_channel(struct rsi_hw *adapter); +struct rsi_hw *rsi_91x_init(void); +void rsi_91x_deinit(struct rsi_hw *adapter); +int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len); +#endif diff --git a/drivers/net/wireless/rsi/rsi_debugfs.h b/drivers/net/wireless/rsi/rsi_debugfs.h new file mode 100644 index 00000000000..580ad3b3f71 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_debugfs.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 __RSI_DEBUGFS_H__ +#define __RSI_DEBUGFS_H__ + +#include "rsi_main.h" +#include <linux/debugfs.h> + +#ifndef CONFIG_RSI_DEBUGFS +static inline int rsi_init_dbgfs(struct rsi_hw *adapter) +{ + return 0; +} + +static inline void rsi_remove_dbgfs(struct rsi_hw *adapter) +{ + return; +} +#else +struct rsi_dbg_files { + const char *name; + umode_t perms; + const struct file_operations fops; +}; + +struct rsi_debugfs { + struct dentry *subdir; + struct rsi_dbg_ops *dfs_get_ops; + struct dentry *rsi_files[MAX_DEBUGFS_ENTRIES]; +}; +int rsi_init_dbgfs(struct rsi_hw *adapter); +void rsi_remove_dbgfs(struct rsi_hw *adapter); +#endif +#endif diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h new file mode 100644 index 00000000000..2cb73e7edb9 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_main.h @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 __RSI_MAIN_H__ +#define __RSI_MAIN_H__ + +#include <linux/string.h> +#include <linux/skbuff.h> +#include <net/mac80211.h> + +#define ERR_ZONE BIT(0) /* For Error Msgs */ +#define INFO_ZONE BIT(1) /* For General Status Msgs */ +#define INIT_ZONE BIT(2) /* For Driver Init Seq Msgs */ +#define MGMT_TX_ZONE BIT(3) /* For TX Mgmt Path Msgs */ +#define MGMT_RX_ZONE BIT(4) /* For RX Mgmt Path Msgs */ +#define DATA_TX_ZONE BIT(5) /* For TX Data Path Msgs */ +#define DATA_RX_ZONE BIT(6) /* For RX Data Path Msgs */ +#define FSM_ZONE BIT(7) /* For State Machine Msgs */ +#define ISR_ZONE BIT(8) /* For Interrupt Msgs */ + +#define FSM_CARD_NOT_READY 0 +#define FSM_BOOT_PARAMS_SENT 1 +#define FSM_EEPROM_READ_MAC_ADDR 2 +#define FSM_RESET_MAC_SENT 3 +#define FSM_RADIO_CAPS_SENT 4 +#define FSM_BB_RF_PROG_SENT 5 +#define FSM_MAC_INIT_DONE 6 + +extern u32 rsi_zone_enabled; +extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...); + +#define RSI_MAX_VIFS 1 +#define NUM_EDCA_QUEUES 4 +#define IEEE80211_ADDR_LEN 6 +#define FRAME_DESC_SZ 16 +#define MIN_802_11_HDR_LEN 24 + +#define DATA_QUEUE_WATER_MARK 400 +#define MIN_DATA_QUEUE_WATER_MARK 300 +#define MULTICAST_WATER_MARK 200 +#define MAC_80211_HDR_FRAME_CONTROL 0 +#define WME_NUM_AC 4 +#define NUM_SOFT_QUEUES 5 +#define MAX_HW_QUEUES 8 +#define INVALID_QUEUE 0xff +#define MAX_CONTINUOUS_VO_PKTS 8 +#define MAX_CONTINUOUS_VI_PKTS 4 + +/* Queue information */ +#define RSI_WIFI_MGMT_Q 0x4 +#define RSI_WIFI_DATA_Q 0x5 +#define IEEE80211_MGMT_FRAME 0x00 +#define IEEE80211_CTL_FRAME 0x04 + +#define IEEE80211_QOS_TID 0x0f +#define IEEE80211_NONQOS_TID 16 + +#define MAX_DEBUGFS_ENTRIES 4 + +#define TID_TO_WME_AC(_tid) ( \ + ((_tid) == 0 || (_tid) == 3) ? BE_Q : \ + ((_tid) < 3) ? BK_Q : \ + ((_tid) < 6) ? VI_Q : \ + VO_Q) + +#define WME_AC(_q) ( \ + ((_q) == BK_Q) ? IEEE80211_AC_BK : \ + ((_q) == BE_Q) ? IEEE80211_AC_BE : \ + ((_q) == VI_Q) ? IEEE80211_AC_VI : \ + IEEE80211_AC_VO) + +struct version_info { + u16 major; + u16 minor; + u16 release_num; + u16 patch_num; +} __packed; + +struct skb_info { + s8 rssi; + u32 flags; + u16 channel; + s8 tid; + s8 sta_id; +}; + +enum edca_queue { + BK_Q, + BE_Q, + VI_Q, + VO_Q, + MGMT_SOFT_Q +}; + +struct security_info { + bool security_enable; + u32 ptk_cipher; + u32 gtk_cipher; +}; + +struct wmm_qinfo { + s32 weight; + s32 wme_params; + s32 pkt_contended; +}; + +struct transmit_q_stats { + u32 total_tx_pkt_send[NUM_EDCA_QUEUES + 1]; + u32 total_tx_pkt_freed[NUM_EDCA_QUEUES + 1]; +}; + +struct vif_priv { + bool is_ht; + bool sgi; + u16 seq_start; +}; + +struct rsi_event { + atomic_t event_condition; + wait_queue_head_t event_queue; +}; + +struct rsi_thread { + void (*thread_function)(void *); + struct completion completion; + struct task_struct *task; + struct rsi_event event; + atomic_t thread_done; +}; + +struct rsi_hw; + +struct rsi_common { + struct rsi_hw *priv; + struct vif_priv vif_info[RSI_MAX_VIFS]; + + bool mgmt_q_block; + struct version_info driver_ver; + struct version_info fw_ver; + + struct rsi_thread tx_thread; + struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 1]; + /* Mutex declaration */ + struct mutex mutex; + /* Mutex used between tx/rx threads */ + struct mutex tx_rxlock; + u8 endpoint; + + /* Channel/band related */ + u8 band; + u8 channel_width; + + u16 rts_threshold; + u16 bitrate_mask[2]; + u32 fixedrate_mask[2]; + + u8 rf_reset; + struct transmit_q_stats tx_stats; + struct security_info secinfo; + struct wmm_qinfo tx_qinfo[NUM_EDCA_QUEUES]; + struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES]; + u8 mac_addr[IEEE80211_ADDR_LEN]; + + /* state related */ + u32 fsm_state; + bool init_done; + u8 bb_rf_prog_count; + bool iface_down; + + /* Generic */ + u8 channel; + u8 *rx_data_pkt; + u8 mac_id; + u8 radio_id; + u16 rate_pwr[20]; + u16 min_rate; + + /* WMM algo related */ + u8 selected_qnum; + u32 pkt_cnt; + u8 min_weight; +}; + +struct rsi_hw { + struct rsi_common *priv; + struct ieee80211_hw *hw; + struct ieee80211_vif *vifs[RSI_MAX_VIFS]; + struct ieee80211_tx_queue_params edca_params[NUM_EDCA_QUEUES]; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + + struct device *device; + u8 sc_nvifs; + +#ifdef CONFIG_RSI_DEBUGFS + struct rsi_debugfs *dfsentry; + u8 num_debugfs_entries; +#endif + void *rsi_dev; + int (*host_intf_read_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len); + int (*host_intf_write_pkt)(struct rsi_hw *adapter, u8 *pkt, u32 len); + int (*check_hw_queue_status)(struct rsi_hw *adapter, u8 q_num); + int (*rx_urb_submit)(struct rsi_hw *adapter); + int (*determine_event_timeout)(struct rsi_hw *adapter); +}; +#endif diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h new file mode 100644 index 00000000000..ac67c4ad63c --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -0,0 +1,285 @@ +/** + * Copyright (c) 2014 Redpine Signals 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 __RSI_MGMT_H__ +#define __RSI_MGMT_H__ + +#include <linux/sort.h> +#include "rsi_boot_params.h" +#include "rsi_main.h" + +#define MAX_MGMT_PKT_SIZE 512 +#define RSI_NEEDED_HEADROOM 80 +#define RSI_RCV_BUFFER_LEN 2000 + +#define RSI_11B_MODE 0 +#define RSI_11G_MODE BIT(7) +#define RETRY_COUNT 8 +#define RETRY_LONG 4 +#define RETRY_SHORT 7 +#define WMM_SHORT_SLOT_TIME 9 +#define SIFS_DURATION 16 + +#define KEY_TYPE_CLEAR 0 +#define RSI_PAIRWISE_KEY 1 +#define RSI_GROUP_KEY 2 + +/* EPPROM_READ_ADDRESS */ +#define WLAN_MAC_EEPROM_ADDR 40 +#define WLAN_MAC_MAGIC_WORD_LEN 0x01 +#define WLAN_HOST_MODE_LEN 0x04 +#define WLAN_FW_VERSION_LEN 0x08 +#define MAGIC_WORD 0x5A + +/* Receive Frame Types */ +#define TA_CONFIRM_TYPE 0x01 +#define RX_DOT11_MGMT 0x02 +#define TX_STATUS_IND 0x04 +#define PROBEREQ_CONFIRM 2 +#define CARD_READY_IND 0x00 + +#define RSI_DELETE_PEER 0x0 +#define RSI_ADD_PEER 0x1 +#define START_AMPDU_AGGR 0x1 +#define STOP_AMPDU_AGGR 0x0 +#define INTERNAL_MGMT_PKT 0x99 + +#define PUT_BBP_RESET 0 +#define BBP_REG_WRITE 0 +#define RF_RESET_ENABLE BIT(3) +#define RATE_INFO_ENABLE BIT(0) +#define RSI_BROADCAST_PKT BIT(9) + +#define UPPER_20_ENABLE (0x2 << 12) +#define LOWER_20_ENABLE (0x4 << 12) +#define FULL40M_ENABLE 0x6 + +#define RSI_LMAC_CLOCK_80MHZ 0x1 +#define RSI_ENABLE_40MHZ (0x1 << 3) + +#define RX_BA_INDICATION 1 +#define RSI_TBL_SZ 40 +#define MAX_RETRIES 8 + +#define STD_RATE_MCS7 0x07 +#define STD_RATE_MCS6 0x06 +#define STD_RATE_MCS5 0x05 +#define STD_RATE_MCS4 0x04 +#define STD_RATE_MCS3 0x03 +#define STD_RATE_MCS2 0x02 +#define STD_RATE_MCS1 0x01 +#define STD_RATE_MCS0 0x00 +#define STD_RATE_54 0x6c +#define STD_RATE_48 0x60 +#define STD_RATE_36 0x48 +#define STD_RATE_24 0x30 +#define STD_RATE_18 0x24 +#define STD_RATE_12 0x18 +#define STD_RATE_11 0x16 +#define STD_RATE_09 0x12 +#define STD_RATE_06 0x0C +#define STD_RATE_5_5 0x0B +#define STD_RATE_02 0x04 +#define STD_RATE_01 0x02 + +#define RSI_RF_TYPE 1 +#define RSI_RATE_00 0x00 +#define RSI_RATE_1 0x0 +#define RSI_RATE_2 0x2 +#define RSI_RATE_5_5 0x4 +#define RSI_RATE_11 0x6 +#define RSI_RATE_6 0x8b +#define RSI_RATE_9 0x8f +#define RSI_RATE_12 0x8a +#define RSI_RATE_18 0x8e +#define RSI_RATE_24 0x89 +#define RSI_RATE_36 0x8d +#define RSI_RATE_48 0x88 +#define RSI_RATE_54 0x8c +#define RSI_RATE_MCS0 0x100 +#define RSI_RATE_MCS1 0x101 +#define RSI_RATE_MCS2 0x102 +#define RSI_RATE_MCS3 0x103 +#define RSI_RATE_MCS4 0x104 +#define RSI_RATE_MCS5 0x105 +#define RSI_RATE_MCS6 0x106 +#define RSI_RATE_MCS7 0x107 +#define RSI_RATE_MCS7_SG 0x307 + +#define BW_20MHZ 0 +#define BW_40MHZ 1 + +#define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\ + FIF_BCN_PRBRESP_PROMISC) +enum opmode { + STA_OPMODE = 1, + AP_OPMODE = 2 +}; + +extern struct ieee80211_rate rsi_rates[12]; +extern const u16 rsi_mcsrates[8]; + +enum sta_notify_events { + STA_CONNECTED = 0, + STA_DISCONNECTED, + STA_TX_ADDBA_DONE, + STA_TX_DELBA, + STA_RX_ADDBA_DONE, + STA_RX_DELBA +}; + +/* Send Frames Types */ +enum cmd_frame_type { + TX_DOT11_MGMT, + RESET_MAC_REQ, + RADIO_CAPABILITIES, + BB_PROG_VALUES_REQUEST, + RF_PROG_VALUES_REQUEST, + WAKEUP_SLEEP_REQUEST, + SCAN_REQUEST, + TSF_UPDATE, + PEER_NOTIFY, + BLOCK_UNBLOCK, + SET_KEY_REQ, + AUTO_RATE_IND, + BOOTUP_PARAMS_REQUEST, + VAP_CAPABILITIES, + EEPROM_READ_TYPE , + EEPROM_WRITE, + GPIO_PIN_CONFIG , + SET_RX_FILTER, + AMPDU_IND, + STATS_REQUEST_FRAME, + BB_BUF_PROG_VALUES_REQ, + BBP_PROG_IN_TA, + BG_SCAN_PARAMS, + BG_SCAN_PROBE_REQ, + CW_MODE_REQ, + PER_CMD_PKT +}; + +struct rsi_mac_frame { + __le16 desc_word[8]; +} __packed; + +struct rsi_boot_params { + __le16 desc_word[8]; + struct bootup_params bootup_params; +} __packed; + +struct rsi_peer_notify { + __le16 desc_word[8]; + u8 mac_addr[6]; + __le16 command; + __le16 mpdu_density; + __le16 reserved; + __le32 sta_flags; +} __packed; + +struct rsi_vap_caps { + __le16 desc_word[8]; + u8 mac_addr[6]; + __le16 keep_alive_period; + u8 bssid[6]; + __le16 reserved; + __le32 flags; + __le16 frag_threshold; + __le16 rts_threshold; + __le32 default_mgmt_rate; + __le32 default_ctrl_rate; + __le32 default_data_rate; + __le16 beacon_interval; + __le16 dtim_period; +} __packed; + +struct rsi_set_key { + __le16 desc_word[8]; + u8 key[4][32]; + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; +} __packed; + +struct rsi_auto_rate { + __le16 desc_word[8]; + __le16 failure_limit; + __le16 initial_boundary; + __le16 max_threshold_limt; + __le16 num_supported_rates; + __le16 aarf_rssi; + __le16 moderate_rate_inx; + __le16 collision_tolerance; + __le16 supported_rates[40]; +} __packed; + +struct qos_params { + __le16 cont_win_min_q; + __le16 cont_win_max_q; + __le16 aifsn_val_q; + __le16 txop_q; +} __packed; + +struct rsi_radio_caps { + __le16 desc_word[8]; + struct qos_params qos_params[MAX_HW_QUEUES]; + u8 num_11n_rates; + u8 num_11ac_rates; + __le16 gcpd_per_rate[20]; +} __packed; + +static inline u32 rsi_get_queueno(u8 *addr, u16 offset) +{ + return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12; +} + +static inline u32 rsi_get_length(u8 *addr, u16 offset) +{ + return (le16_to_cpu(*(__le16 *)&addr[offset])) & 0x0fff; +} + +static inline u8 rsi_get_extended_desc(u8 *addr, u16 offset) +{ + return le16_to_cpu(*((__le16 *)&addr[offset + 4])) & 0x00ff; +} + +static inline u8 rsi_get_rssi(u8 *addr) +{ + return *(u8 *)(addr + FRAME_DESC_SZ); +} + +static inline u8 rsi_get_channel(u8 *addr) +{ + return *(char *)(addr + 15); +} + +int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg); +int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode); +int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid, + u16 ssn, u8 buf_size, u8 event); +int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len, + u8 key_type, u8 key_id, u32 cipher); +int rsi_set_channel(struct rsi_common *common, u16 chno); +void rsi_inform_bss_status(struct rsi_common *common, u8 status, + const u8 *bssid, u8 qos_enable, u16 aid); +void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb); +int rsi_mac80211_attach(struct rsi_common *common); +void rsi_indicate_tx_status(struct rsi_hw *common, struct sk_buff *skb, + int status); +bool rsi_is_cipher_wep(struct rsi_common *common); +void rsi_core_qos_processor(struct rsi_common *common); +void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb); +int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb); +int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb); +#endif diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h new file mode 100644 index 00000000000..df4b5e20e05 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_sdio.h @@ -0,0 +1,129 @@ +/** + * @section LICENSE + * Copyright (c) 2014 Redpine Signals 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 __RSI_SDIO_INTF__ +#define __RSI_SDIO_INTF__ + +#include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio_ids.h> +#include "rsi_main.h" + +enum sdio_interrupt_type { + BUFFER_FULL = 0x0, + BUFFER_AVAILABLE = 0x1, + FIRMWARE_ASSERT_IND = 0x3, + MSDU_PACKET_PENDING = 0x4, + UNKNOWN_INT = 0XE +}; + +/* Buffer status register related info */ +#define PKT_BUFF_SEMI_FULL 0 +#define PKT_BUFF_FULL 1 +#define PKT_MGMT_BUFF_FULL 2 +#define MSDU_PKT_PENDING 3 +/* Interrupt Bit Related Macros */ +#define PKT_BUFF_AVAILABLE 0 +#define FW_ASSERT_IND 2 + +#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3 +#define RSI_FN1_INT_REGISTER 0xf9 +#define RSI_SD_REQUEST_MASTER 0x10000 + +/* FOR SD CARD ONLY */ +#define SDIO_RX_NUM_BLOCKS_REG 0x000F1 +#define SDIO_FW_STATUS_REG 0x000F2 +#define SDIO_NXT_RD_DELAY2 0x000F5 +#define SDIO_MASTER_ACCESS_MSBYTE 0x000FA +#define SDIO_MASTER_ACCESS_LSBYTE 0x000FB +#define SDIO_READ_START_LVL 0x000FC +#define SDIO_READ_FIFO_CTL 0x000FD +#define SDIO_WRITE_FIFO_CTL 0x000FE +#define SDIO_FUN1_INTR_CLR_REG 0x0008 +#define SDIO_REG_HIGH_SPEED 0x0013 + +#define RSI_GET_SDIO_INTERRUPT_TYPE(_I, TYPE) \ + { \ + TYPE = \ + (_I & (1 << PKT_BUFF_AVAILABLE)) ? \ + BUFFER_AVAILABLE : \ + (_I & (1 << MSDU_PKT_PENDING)) ? \ + MSDU_PACKET_PENDING : \ + (_I & (1 << FW_ASSERT_IND)) ? \ + FIRMWARE_ASSERT_IND : UNKNOWN_INT; \ + } + +/* common registers in SDIO function1 */ +#define TA_SOFT_RESET_REG 0x0004 +#define TA_TH0_PC_REG 0x0400 +#define TA_HOLD_THREAD_REG 0x0844 +#define TA_RELEASE_THREAD_REG 0x0848 + +#define TA_SOFT_RST_CLR 0 +#define TA_SOFT_RST_SET BIT(0) +#define TA_PC_ZERO 0 +#define TA_HOLD_THREAD_VALUE cpu_to_le32(0xF) +#define TA_RELEASE_THREAD_VALUE cpu_to_le32(0xF) +#define TA_BASE_ADDR 0x2200 +#define MISC_CFG_BASE_ADDR 0x4150 + +struct receive_info { + bool buffer_full; + bool semi_buffer_full; + bool mgmt_buffer_full; + u32 mgmt_buf_full_counter; + u32 buf_semi_full_counter; + u8 watch_bufferfull_count; + u32 sdio_intr_status_zero; + u32 sdio_int_counter; + u32 total_sdio_msdu_pending_intr; + u32 total_sdio_unknown_intr; + u32 buf_full_counter; + u32 buf_avilable_counter; +}; + +struct rsi_91x_sdiodev { + struct sdio_func *pfunction; + struct task_struct *in_sdio_litefi_irq; + struct receive_info rx_info; + u32 next_read_delay; + u32 sdio_high_speed_enable; + u8 sdio_clock_speed; + u32 cardcapability; + u8 prev_desc[16]; + u32 tx_blk_size; + u8 write_fail; +}; + +void rsi_interrupt_handler(struct rsi_hw *adapter); +int rsi_init_sdio_slave_regs(struct rsi_hw *adapter); +int rsi_sdio_device_init(struct rsi_common *common); +int rsi_sdio_read_register(struct rsi_hw *adapter, u32 addr, u8 *data); +int rsi_sdio_host_intf_read_pkt(struct rsi_hw *adapter, u8 *pkt, u32 length); +int rsi_sdio_write_register(struct rsi_hw *adapter, u8 function, + u32 addr, u8 *data); +int rsi_sdio_write_register_multiple(struct rsi_hw *adapter, u32 addr, + u8 *data, u32 count); +void rsi_sdio_ack_intr(struct rsi_hw *adapter, u8 int_bit); +int rsi_sdio_determine_event_timeout(struct rsi_hw *adapter); +int rsi_sdio_read_buffer_status_register(struct rsi_hw *adapter, u8 q_num); +#endif diff --git a/drivers/net/wireless/rsi/rsi_usb.h b/drivers/net/wireless/rsi/rsi_usb.h new file mode 100644 index 00000000000..ebea0c411ea --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_usb.h @@ -0,0 +1,68 @@ +/** + * @section LICENSE + * Copyright (c) 2014 Redpine Signals 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 __RSI_USB_INTF__ +#define __RSI_USB_INTF__ + +#include <linux/usb.h> +#include "rsi_main.h" +#include "rsi_common.h" + +#define USB_INTERNAL_REG_1 0x25000 +#define RSI_USB_READY_MAGIC_NUM 0xab +#define FW_STATUS_REG 0x41050012 + +#define USB_VENDOR_REGISTER_READ 0x15 +#define USB_VENDOR_REGISTER_WRITE 0x16 +#define RSI_USB_TX_HEAD_ROOM 128 + +#define MAX_RX_URBS 1 +#define MAX_BULK_EP 8 +#define MGMT_EP 1 +#define DATA_EP 2 + +struct rsi_91x_usbdev { + struct rsi_thread rx_thread; + u8 endpoint; + struct usb_device *usbdev; + struct usb_interface *pfunction; + struct urb *rx_usb_urb[MAX_RX_URBS]; + u8 *tx_buffer; + __le16 bulkin_size; + u8 bulkin_endpoint_addr; + __le16 bulkout_size[MAX_BULK_EP]; + u8 bulkout_endpoint_addr[MAX_BULK_EP]; + u32 tx_blk_size; + u8 write_fail; +}; + +static inline int rsi_usb_check_queue_status(struct rsi_hw *adapter, u8 q_num) +{ + /* In USB, there isn't any need to check the queue status */ + return QUEUE_NOT_FULL; +} + +static inline int rsi_usb_event_timeout(struct rsi_hw *adapter) +{ + return EVENT_WAIT_FOREVER; +} + +int rsi_usb_device_init(struct rsi_common *common); +int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr, + u8 *data, u32 count); +void rsi_usb_rx_thread(struct rsi_common *common); +#endif diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 959e699702e..0b405b8c8d7 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -148,7 +148,8 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.antenna = (flags2 >> 15) & 1; rx_status.rate_idx = (flags >> 20) & 0xF; agc = (flags2 >> 17) & 0x7F; - if (priv->r8185) { + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) { if (rx_status.rate_idx > 3) signal = 90 - clamp_t(u8, agc, 25, 90); else @@ -288,7 +289,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev, (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | skb->len; - if (priv->r8185) + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) tx_flags |= RTL818X_TX_DESC_FLAG_DMA | RTL818X_TX_DESC_FLAG_NO_ENC; @@ -305,7 +306,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev, rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, info); - if (!priv->r8185) { + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) { unsigned int remainder; plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), @@ -370,6 +371,36 @@ void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } +static void rtl8180_conf_basic_rates(struct ieee80211_hw *dev, + u32 rates_mask) +{ + struct rtl8180_priv *priv = dev->priv; + + u8 max, min; + u16 reg; + + max = fls(rates_mask) - 1; + min = ffs(rates_mask) - 1; + + switch (priv->chip_family) { + + case RTL818X_CHIP_FAMILY_RTL8180: + /* in 8180 this is NOT a BITMAP */ + reg = rtl818x_ioread16(priv, &priv->map->BRSR); + reg &= ~3; + reg |= max; + rtl818x_iowrite16(priv, &priv->map->BRSR, reg); + + break; + + case RTL818X_CHIP_FAMILY_RTL8185: + /* in 8185 this is a BITMAP */ + rtl818x_iowrite16(priv, &priv->map->BRSR, rates_mask); + rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (max << 4) | min); + break; + } +} + static int rtl8180_init_hw(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; @@ -412,7 +443,7 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite8(priv, &priv->map->MSR, 0); - if (!priv->r8185) + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) rtl8180_set_anaparam(priv, priv->anaparam); rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); @@ -425,7 +456,7 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3)); - if (priv->r8185) { + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) { reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4)); } @@ -437,12 +468,9 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); - if (priv->r8185) { + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) { rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); - rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); - - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); /* TODO: set ClkRun enable? necessary? */ reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); @@ -452,7 +480,6 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } else { - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1); rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); @@ -460,8 +487,18 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) } priv->rf->init(dev); - if (priv->r8185) - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + + /* default basic rates are 1,2 Mbps for rtl8180. 1,2,6,9,12,18,24 Mbps + * otherwise. bitmask 0x3 and 0x01f3 respectively. + * NOTE: currenty rtl8225 RF code changes basic rates, so we need to do + * this after rf init. + * TODO: try to find out whether RF code really needs to do this.. + */ + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) + rtl8180_conf_basic_rates(dev, 0x3); + else + rtl8180_conf_basic_rates(dev, 0x1f3); + return 0; } @@ -624,7 +661,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) RTL818X_RX_CONF_BROADCAST | RTL818X_RX_CONF_NICMAC; - if (priv->r8185) + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2; else { reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) @@ -636,7 +673,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) priv->rx_conf = reg; rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - if (priv->r8185) { + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) { reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); /* CW is not on per-packet basis. @@ -668,7 +705,9 @@ static int rtl8180_start(struct ieee80211_hw *dev) reg |= (6 << 21 /* MAX TX DMA */) | RTL818X_TX_CONF_NO_ICV; - if (priv->r8185) + + + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) reg &= ~RTL818X_TX_CONF_PROBE_DTS; else reg &= ~RTL818X_TX_CONF_HW_SEQNUM; @@ -827,6 +866,72 @@ static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) return 0; } +static int rtl8180_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rtl8180_priv *priv = dev->priv; + u8 cw_min, cw_max; + + /* nothing to do ? */ + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) + return 0; + + cw_min = fls(params->cw_min); + cw_max = fls(params->cw_max); + + rtl818x_iowrite8(priv, &priv->map->CW_VAL, (cw_max << 4) | cw_min); + + return 0; +} + +static void rtl8180_conf_erp(struct ieee80211_hw *dev, + struct ieee80211_bss_conf *info) +{ + struct rtl8180_priv *priv = dev->priv; + u8 sifs, difs; + int eifs; + u8 hw_eifs; + + /* TODO: should we do something ? */ + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) + return; + + /* I _hope_ this means 10uS for the HW. + * In reference code it is 0x22 for + * both rtl8187L and rtl8187SE + */ + sifs = 0x22; + + if (info->use_short_slot) + priv->slot_time = 9; + else + priv->slot_time = 20; + + /* 10 is SIFS time in uS */ + difs = 10 + 2 * priv->slot_time; + eifs = 10 + difs + priv->ack_time; + + /* HW should use 4uS units for EIFS (I'm sure for rtl8185)*/ + hw_eifs = DIV_ROUND_UP(eifs, 4); + + + rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); + rtl818x_iowrite8(priv, &priv->map->SIFS, sifs); + rtl818x_iowrite8(priv, &priv->map->DIFS, difs); + + /* from reference code. set ack timeout reg = eifs reg */ + rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, hw_eifs); + + /* rtl8187/rtl8185 HW bug. After EIFS is elapsed, + * the HW still wait for DIFS. + * HW uses 4uS units for EIFS. + */ + hw_eifs = DIV_ROUND_UP(eifs - difs, 4); + + rtl818x_iowrite8(priv, &priv->map->EIFS, hw_eifs); +} + static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -854,8 +959,23 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, rtl818x_iowrite8(priv, &priv->map->MSR, reg); } - if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) - priv->rf->conf_erp(dev, info); + if (changed & BSS_CHANGED_BASIC_RATES) + rtl8180_conf_basic_rates(dev, info->basic_rates); + + if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) { + + /* when preamble changes, acktime duration changes, and erp must + * be recalculated. ACK time is calculated at lowest rate. + * Since mac80211 include SIFS time we remove it (-10) + */ + priv->ack_time = + le16_to_cpu(ieee80211_generic_frame_duration(dev, + priv->vif, + IEEE80211_BAND_2GHZ, 10, + &priv->rates[0])) - 10; + + rtl8180_conf_erp(dev, info); + } if (changed & BSS_CHANGED_BEACON_ENABLED) vif_priv->enable_beacon = info->enable_beacon; @@ -913,6 +1033,7 @@ static const struct ieee80211_ops rtl8180_ops = { .remove_interface = rtl8180_remove_interface, .config = rtl8180_config, .bss_info_changed = rtl8180_bss_info_changed, + .conf_tx = rtl8180_conf_tx, .prepare_multicast = rtl8180_prepare_multicast, .configure_filter = rtl8180_configure_filter, .get_tsf = rtl8180_get_tsf, @@ -920,8 +1041,7 @@ static const struct ieee80211_ops rtl8180_ops = { static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) { - struct ieee80211_hw *dev = eeprom->data; - struct rtl8180_priv *priv = dev->priv; + struct rtl8180_priv *priv = eeprom->data; u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; @@ -932,8 +1052,7 @@ static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) { - struct ieee80211_hw *dev = eeprom->data; - struct rtl8180_priv *priv = dev->priv; + struct rtl8180_priv *priv = eeprom->data; u8 reg = 2 << 6; if (eeprom->reg_data_in) @@ -950,6 +1069,67 @@ static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) udelay(10); } +static void rtl8180_eeprom_read(struct rtl8180_priv *priv) +{ + struct eeprom_93cx6 eeprom; + int eeprom_cck_table_adr; + u16 eeprom_val; + int i; + + eeprom.data = priv; + eeprom.register_read = rtl8180_eeprom_register_read; + eeprom.register_write = rtl8180_eeprom_register_write; + if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) + eeprom.width = PCI_EEPROM_WIDTH_93C66; + else + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_PROGRAM); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); + eeprom_val &= 0xFF; + priv->rf_type = eeprom_val; + + eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); + priv->csthreshold = eeprom_val >> 8; + + eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)priv->mac_addr, 3); + + eeprom_cck_table_adr = 0x10; + + /* CCK TX power */ + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, eeprom_cck_table_adr + (i >> 1), + &txpwr); + priv->channels[i].hw_value = txpwr & 0xFF; + priv->channels[i + 1].hw_value = txpwr >> 8; + } + + /* OFDM TX power */ + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) { + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); + priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; + priv->channels[i + 1].hw_value |= txpwr & 0xFF00; + } + } + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) { + __le32 anaparam; + eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); + priv->anaparam = le32_to_cpu(anaparam); + eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); +} + static int rtl8180_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -957,12 +1137,9 @@ static int rtl8180_probe(struct pci_dev *pdev, struct rtl8180_priv *priv; unsigned long mem_addr, mem_len; unsigned int io_addr, io_len; - int err, i; - struct eeprom_93cx6 eeprom; + int err; const char *chip_name, *rf_name = NULL; u32 reg; - u16 eeprom_val; - u8 mac_addr[ETH_ALEN]; err = pci_enable_device(pdev); if (err) { @@ -1052,15 +1229,22 @@ static int rtl8180_probe(struct pci_dev *pdev, switch (reg) { case RTL818X_TX_CONF_R8180_ABCD: chip_name = "RTL8180"; + priv->chip_family = RTL818X_CHIP_FAMILY_RTL8180; break; + case RTL818X_TX_CONF_R8180_F: chip_name = "RTL8180vF"; + priv->chip_family = RTL818X_CHIP_FAMILY_RTL8180; break; + case RTL818X_TX_CONF_R8185_ABC: chip_name = "RTL8185"; + priv->chip_family = RTL818X_CHIP_FAMILY_RTL8185; break; + case RTL818X_TX_CONF_R8185_D: chip_name = "RTL8185vD"; + priv->chip_family = RTL818X_CHIP_FAMILY_RTL8185; break; default: printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", @@ -1068,27 +1252,14 @@ static int rtl8180_probe(struct pci_dev *pdev, goto err_iounmap; } - priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; - if (priv->r8185) { + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) { priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); pci_try_set_mwi(pdev); } - eeprom.data = dev; - eeprom.register_read = rtl8180_eeprom_register_read; - eeprom.register_write = rtl8180_eeprom_register_write; - if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) - eeprom.width = PCI_EEPROM_WIDTH_93C66; - else - eeprom.width = PCI_EEPROM_WIDTH_93C46; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); + rtl8180_eeprom_read(priv); - eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); - eeprom_val &= 0xFF; - switch (eeprom_val) { + switch (priv->rf_type) { case 1: rf_name = "Intersil"; break; case 2: rf_name = "RFMD"; @@ -1106,7 +1277,7 @@ static int rtl8180_probe(struct pci_dev *pdev, break; default: printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", - pci_name(pdev), eeprom_val); + pci_name(pdev), priv->rf_type); goto err_iounmap; } @@ -1116,42 +1287,12 @@ static int rtl8180_probe(struct pci_dev *pdev, goto err_iounmap; } - eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); - priv->csthreshold = eeprom_val >> 8; - if (!priv->r8185) { - __le32 anaparam; - eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); - priv->anaparam = le32_to_cpu(anaparam); - eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); - } - - eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3); - if (!is_valid_ether_addr(mac_addr)) { + if (!is_valid_ether_addr(priv->mac_addr)) { printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" " randomly generated MAC addr\n", pci_name(pdev)); - eth_random_addr(mac_addr); + eth_random_addr(priv->mac_addr); } - SET_IEEE80211_PERM_ADDR(dev, mac_addr); - - /* CCK TX power */ - for (i = 0; i < 14; i += 2) { - u16 txpwr; - eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); - priv->channels[i].hw_value = txpwr & 0xFF; - priv->channels[i + 1].hw_value = txpwr >> 8; - } - - /* OFDM TX power */ - if (priv->r8185) { - for (i = 0; i < 14; i += 2) { - u16 txpwr; - eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); - priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; - priv->channels[i + 1].hw_value |= txpwr & 0xFF00; - } - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + SET_IEEE80211_PERM_ADDR(dev, priv->mac_addr); spin_lock_init(&priv->lock); @@ -1163,7 +1304,7 @@ static int rtl8180_probe(struct pci_dev *pdev, } wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n", - mac_addr, chip_name, priv->rf->name); + priv->mac_addr, chip_name, priv->rf->name); return 0; diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h index 30523314da4..26383d77fc3 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h @@ -81,12 +81,18 @@ struct rtl8180_priv { struct ieee80211_supported_band band; struct pci_dev *pdev; u32 rx_conf; + u8 slot_time; + u16 ack_time; - int r8185; + enum { + RTL818X_CHIP_FAMILY_RTL8180, + RTL818X_CHIP_FAMILY_RTL8185, + } chip_family; u32 anaparam; u16 rfparam; u8 csthreshold; - + u8 mac_addr[ETH_ALEN]; + u8 rf_type; /* sequence # */ u16 seqno; }; diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c index d60a5f39902..1c0fe238d99 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c @@ -730,32 +730,11 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, msleep(10); } -static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev, - struct ieee80211_bss_conf *info) -{ - struct rtl8180_priv *priv = dev->priv; - - if (info->use_short_slot) { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); - rtl818x_iowrite8(priv, &priv->map->EIFS, 81); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); - } else { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); - rtl818x_iowrite8(priv, &priv->map->EIFS, 81); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); - } -} - static const struct rtl818x_rf_ops rtl8225_ops = { .name = "rtl8225", .init = rtl8225_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel, - .conf_erp = rtl8225_rf_conf_erp, }; static const struct rtl818x_rf_ops rtl8225z2_ops = { @@ -763,7 +742,6 @@ static const struct rtl818x_rf_ops rtl8225z2_ops = { .init = rtl8225z2_rf_init, .stop = rtl8225_rf_stop, .set_chan = rtl8225_rf_set_channel, - .conf_erp = rtl8225_rf_conf_erp, }; const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev) diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h index fa7f7f61ea2..1815b15d03b 100644 --- a/drivers/net/wireless/rtl818x/rtl818x.h +++ b/drivers/net/wireless/rtl818x/rtl818x.h @@ -192,7 +192,6 @@ struct rtl818x_rf_ops { void (*init)(struct ieee80211_hw *); void (*stop)(struct ieee80211_hw *); void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *); - void (*conf_erp)(struct ieee80211_hw *, struct ieee80211_bss_conf *); u8 (*calc_rssi)(u8 agc, u8 sq); }; diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index fe20e1cc054..65d4ca19d13 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -26,6 +26,18 @@ config NFC_WILINK Say Y here to compile support for Texas Instrument's NFC WiLink driver into the kernel or say M to compile it as module. +config NFC_TRF7970A + tristate "Texas Instruments TRF7970a NFC driver" + depends on SPI && NFC_DIGITAL + help + This option enables the NFC driver for Texas Instruments' TRF7970a + device. Such device supports 5 different protocols: ISO14443A, + ISO14443B, FeLiCa, ISO15693 and ISO18000-3. + + Say Y here to compile support for TRF7970a into the kernel or + say M to compile it as a module. The module will be called + trf7970a.ko. + config NFC_MEI_PHY tristate "MEI bus NFC device support" depends on INTEL_MEI && NFC_HCI diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 56ab822ba03..ae42a3fa60c 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -10,5 +10,6 @@ obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ +obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index cf1a87bb74f..d46a700a963 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -55,26 +55,14 @@ NFC_PROTO_NFC_DEP_MASK) static const struct usb_device_id pn533_table[] = { - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = PN533_VENDOR_ID, - .idProduct = PN533_PRODUCT_ID, - .driver_info = PN533_DEVICE_STD, - }, - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = SCM_VENDOR_ID, - .idProduct = SCL3711_PRODUCT_ID, - .driver_info = PN533_DEVICE_STD, - }, - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = SONY_VENDOR_ID, - .idProduct = PASORI_PRODUCT_ID, - .driver_info = PN533_DEVICE_PASORI, - }, - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = ACS_VENDOR_ID, - .idProduct = ACR122U_PRODUCT_ID, - .driver_info = PN533_DEVICE_ACR122U, - }, + { USB_DEVICE(PN533_VENDOR_ID, PN533_PRODUCT_ID), + .driver_info = PN533_DEVICE_STD }, + { USB_DEVICE(SCM_VENDOR_ID, SCL3711_PRODUCT_ID), + .driver_info = PN533_DEVICE_STD }, + { USB_DEVICE(SONY_VENDOR_ID, PASORI_PRODUCT_ID), + .driver_info = PN533_DEVICE_PASORI }, + { USB_DEVICE(ACS_VENDOR_ID, ACR122U_PRODUCT_ID), + .driver_info = PN533_DEVICE_ACR122U }, { } }; MODULE_DEVICE_TABLE(usb, pn533_table); diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index d6185ff2f87..f2acd85be86 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -58,8 +58,19 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" +/* + * Exposed through the 4 most significant bytes + * from the HCI SW_VERSION first byte, a.k.a. + * SW RomLib. + */ +#define PN544_HW_VARIANT_C2 0xa +#define PN544_HW_VARIANT_C3 0xb + +#define PN544_FW_CMD_RESET 0x01 #define PN544_FW_CMD_WRITE 0x08 #define PN544_FW_CMD_CHECK 0x06 +#define PN544_FW_CMD_SECURE_WRITE 0x0C +#define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D struct pn544_i2c_fw_frame_write { u8 cmd; @@ -88,13 +99,31 @@ struct pn544_i2c_fw_blob { u8 data[]; }; +struct pn544_i2c_fw_secure_frame { + u8 cmd; + u16 be_datalen; + u8 data[]; +} __packed; + +struct pn544_i2c_fw_secure_blob { + u64 header; + u8 data[]; +}; + #define PN544_FW_CMD_RESULT_TIMEOUT 0x01 #define PN544_FW_CMD_RESULT_BAD_CRC 0x02 #define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08 #define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B #define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11 +#define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13 #define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18 +#define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19 +#define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D +#define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20 +#define PN544_FW_CMD_RESULT_CHUNK_OK 0x21 #define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74 +#define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0 +#define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) @@ -104,11 +133,17 @@ struct pn544_i2c_fw_blob { #define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\ PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\ PN544_FW_WRITE_BUFFER_MAX_LEN) +#define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3 +#define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\ + PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN) +#define PN544_FW_SECURE_FRAME_HEADER_LEN 3 +#define PN544_FW_SECURE_BLOB_HEADER_LEN 8 #define FW_WORK_STATE_IDLE 1 #define FW_WORK_STATE_START 2 #define FW_WORK_STATE_WAIT_WRITE_ANSWER 3 #define FW_WORK_STATE_WAIT_CHECK_ANSWER 4 +#define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5 struct pn544_i2c_phy { struct i2c_client *i2c_dev; @@ -119,6 +154,8 @@ struct pn544_i2c_phy { unsigned int gpio_fw; unsigned int en_polarity; + u8 hw_variant; + struct work_struct fw_work; int fw_work_state; char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; @@ -127,6 +164,8 @@ struct pn544_i2c_phy { size_t fw_blob_size; const u8 *fw_blob_data; size_t fw_written; + size_t fw_size; + int fw_cmd_result; int powered; @@ -390,6 +429,8 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) switch (response.status) { case 0: return 0; + case PN544_FW_CMD_RESULT_CHUNK_OK: + return response.status; case PN544_FW_CMD_RESULT_TIMEOUT: return -ETIMEDOUT; case PN544_FW_CMD_RESULT_BAD_CRC: @@ -400,9 +441,20 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) return -EPROTO; case PN544_FW_CMD_RESULT_INVALID_PARAMETER: return -EINVAL; + case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND: + return -ENOTSUPP; case PN544_FW_CMD_RESULT_INVALID_LENGTH: return -EBADMSG; + case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR: + return -ENOKEY; + case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR: + return -EINVAL; + case PN544_FW_CMD_RESULT_MEMORY_ERROR: + return -ENOMEM; + case PN544_FW_CMD_RESULT_COMMAND_REJECTED: + return -EACCES; case PN544_FW_CMD_RESULT_WRITE_FAILED: + case PN544_FW_CMD_RESULT_CHUNK_ERROR: return -EIO; default: return -EIO; @@ -469,7 +521,8 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = pn544_hci_i2c_disable, }; -static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name) +static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name, + u8 hw_variant) { struct pn544_i2c_phy *phy = phy_id; @@ -477,6 +530,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name) strcpy(phy->firmware_name, firmware_name); + phy->hw_variant = hw_variant; phy->fw_work_state = FW_WORK_STATE_START; schedule_work(&phy->fw_work); @@ -598,12 +652,93 @@ static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy) return 0; } +static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy, + const u8 *data, u16 datalen) +{ + u8 buf[PN544_FW_I2C_MAX_PAYLOAD]; + struct pn544_i2c_fw_secure_frame *chunk; + int chunklen; + int r; + + if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN) + datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN; + + chunk = (struct pn544_i2c_fw_secure_frame *) buf; + + chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE; + + put_unaligned_be16(datalen, &chunk->be_datalen); + + memcpy(chunk->data, data, datalen); + + chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen; + + r = i2c_master_send(phy->i2c_dev, buf, chunklen); + + if (r == chunklen) + return datalen; + else if (r < 0) + return r; + else + return -EIO; + +} + +static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy) +{ + struct pn544_i2c_fw_secure_frame *framep; + int r; + + framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data; + if (phy->fw_written == 0) + phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen) + + PN544_FW_SECURE_FRAME_HEADER_LEN; + + /* Only secure write command can be chunked*/ + if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD && + framep->cmd != PN544_FW_CMD_SECURE_WRITE) + return -EINVAL; + + /* The firmware also have other commands, we just send them directly */ + if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) { + r = i2c_master_send(phy->i2c_dev, + (const char *) phy->fw_blob_data, phy->fw_blob_size); + + if (r == phy->fw_blob_size) + goto exit; + else if (r < 0) + return r; + else + return -EIO; + } + + r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy, + phy->fw_blob_data + phy->fw_written, + phy->fw_blob_size - phy->fw_written); + if (r < 0) + return r; + +exit: + phy->fw_written += r; + phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER; + + /* SW reset command will not trig any response from PN544 */ + if (framep->cmd == PN544_FW_CMD_RESET) { + pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE); + phy->fw_cmd_result = 0; + schedule_work(&phy->fw_work); + } + + return 0; +} + static void pn544_hci_i2c_fw_work(struct work_struct *work) { struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy, fw_work); int r; struct pn544_i2c_fw_blob *blob; + struct pn544_i2c_fw_secure_blob *secure_blob; switch (phy->fw_work_state) { case FW_WORK_STATE_START: @@ -614,13 +749,29 @@ static void pn544_hci_i2c_fw_work(struct work_struct *work) if (r < 0) goto exit_state_start; - blob = (struct pn544_i2c_fw_blob *) phy->fw->data; - phy->fw_blob_size = get_unaligned_be32(&blob->be_size); - phy->fw_blob_dest_addr = get_unaligned_be32(&blob->be_destaddr); - phy->fw_blob_data = blob->data; - phy->fw_written = 0; - r = pn544_hci_i2c_fw_write_chunk(phy); + + switch (phy->hw_variant) { + case PN544_HW_VARIANT_C2: + blob = (struct pn544_i2c_fw_blob *) phy->fw->data; + phy->fw_blob_size = get_unaligned_be32(&blob->be_size); + phy->fw_blob_dest_addr = get_unaligned_be32( + &blob->be_destaddr); + phy->fw_blob_data = blob->data; + + r = pn544_hci_i2c_fw_write_chunk(phy); + break; + case PN544_HW_VARIANT_C3: + secure_blob = (struct pn544_i2c_fw_secure_blob *) + phy->fw->data; + phy->fw_blob_data = secure_blob->data; + phy->fw_size = phy->fw->size; + r = pn544_hci_i2c_fw_secure_write_frame(phy); + break; + default: + r = -ENOTSUPP; + break; + } exit_state_start: if (r < 0) @@ -672,6 +823,35 @@ exit_state_wait_check_answer: pn544_hci_i2c_fw_work_complete(phy, r); break; + case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER: + r = phy->fw_cmd_result; + if (r < 0) + goto exit_state_wait_secure_write_answer; + + if (r == PN544_FW_CMD_RESULT_CHUNK_OK) { + r = pn544_hci_i2c_fw_secure_write_frame(phy); + goto exit_state_wait_secure_write_answer; + } + + if (phy->fw_written == phy->fw_blob_size) { + secure_blob = (struct pn544_i2c_fw_secure_blob *) + (phy->fw_blob_data + phy->fw_blob_size); + phy->fw_size -= phy->fw_blob_size + + PN544_FW_SECURE_BLOB_HEADER_LEN; + if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN + + PN544_FW_SECURE_FRAME_HEADER_LEN) { + phy->fw_blob_data = secure_blob->data; + + phy->fw_written = 0; + r = pn544_hci_i2c_fw_secure_write_frame(phy); + } + } + +exit_state_wait_secure_write_answer: + if (r < 0 || phy->fw_size == 0) + pn544_hci_i2c_fw_work_complete(phy, r); + break; + default: break; } diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 3df4a109cfa..9c8051d20ce 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -786,7 +786,7 @@ static int pn544_hci_fw_download(struct nfc_hci_dev *hdev, if (info->fw_download == NULL) return -ENOTSUPP; - return info->fw_download(info->phy_id, firmware_name); + return info->fw_download(info->phy_id, firmware_name, hdev->sw_romlib); } static int pn544_hci_discover_se(struct nfc_hci_dev *hdev) diff --git a/drivers/nfc/pn544/pn544.h b/drivers/nfc/pn544/pn544.h index 491bf45da35..2aa9233e808 100644 --- a/drivers/nfc/pn544/pn544.h +++ b/drivers/nfc/pn544/pn544.h @@ -25,7 +25,8 @@ #define PN544_HCI_MODE 0 #define PN544_FW_MODE 1 -typedef int (*fw_download_t)(void *context, const char *firmware_name); +typedef int (*fw_download_t)(void *context, const char *firmware_name, + u8 hw_variant); int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, int phy_headroom, int phy_tailroom, int phy_payload, diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index a8555f81cbb..b7a372af5eb 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -27,7 +27,8 @@ #define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ NFC_PROTO_MIFARE_MASK | \ NFC_PROTO_FELICA_MASK | \ - NFC_PROTO_NFC_DEP_MASK) + NFC_PROTO_NFC_DEP_MASK | \ + NFC_PROTO_ISO14443_MASK) #define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ NFC_DIGITAL_DRV_CAPS_TG_CRC) @@ -139,6 +140,8 @@ static const struct port100_in_rf_setting in_rf_settings[] = { .in_recv_set_number = 15, .in_recv_comm_type = PORT100_COMM_TYPE_IN_106A, }, + /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */ + [NFC_DIGITAL_RF_TECH_LAST] = { 0 }, }; /** @@ -174,6 +177,9 @@ static const struct port100_tg_rf_setting tg_rf_settings[] = { .tg_set_number = 8, .tg_comm_type = PORT100_COMM_TYPE_TG_424F, }, + /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */ + [NFC_DIGITAL_RF_TECH_LAST] = { 0 }, + }; #define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00 @@ -293,6 +299,10 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { { PORT100_IN_PROT_CHECK_CRC, 0 }, { PORT100_IN_PROT_END, 0 }, }, + [NFC_DIGITAL_FRAMING_NFCA_T4T] = { + /* nfc_digital_framing_nfca_standard_with_crc_a */ + { PORT100_IN_PROT_END, 0 }, + }, [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = { /* nfc_digital_framing_nfca_standard */ { PORT100_IN_PROT_END, 0 }, @@ -330,6 +340,10 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { { PORT100_IN_PROT_END, 0 }, }, + /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */ + [NFC_DIGITAL_FRAMING_LAST] = { + { PORT100_IN_PROT_END, 0 }, + }, }; static struct port100_protocol @@ -371,6 +385,10 @@ tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = { { PORT100_TG_PROT_RF_OFF, 1 }, { PORT100_TG_PROT_END, 0 }, }, + /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */ + [NFC_DIGITAL_FRAMING_LAST] = { + { PORT100_TG_PROT_END, 0 }, + }, }; struct port100 { @@ -1356,10 +1374,7 @@ static struct nfc_digital_ops port100_digital_ops = { }; static const struct usb_device_id port100_table[] = { - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = SONY_VENDOR_ID, - .idProduct = RCS380_PRODUCT_ID, - }, + { USB_DEVICE(SONY_VENDOR_ID, RCS380_PRODUCT_ID), }, { } }; MODULE_DEVICE_TABLE(usb, port100_table); diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c new file mode 100644 index 00000000000..d9babe98647 --- /dev/null +++ b/drivers/nfc/trf7970a.c @@ -0,0 +1,1370 @@ +/* + * TI TRF7970a RFID/NFC Transceiver Driver + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Erick Macias <emacias@ti.com> + * Author: Felipe Balbi <balbi@ti.com> + * Author: Mark A. Greer <mgreer@animalcreek.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/interrupt.h> +#include <linux/nfc.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> + +#include <net/nfc/nfc.h> +#include <net/nfc/digital.h> + +/* There are 3 ways the host can communicate with the trf7970a: + * parallel mode, SPI with Slave Select (SS) mode, and SPI without + * SS mode. The driver only supports the two SPI modes. + * + * The trf7970a is very timing sensitive and the VIN, EN2, and EN + * pins must asserted in that order and with specific delays in between. + * The delays used in the driver were provided by TI and have been + * confirmed to work with this driver. + * + * Timeouts are implemented using the delayed workqueue kernel facility. + * Timeouts are required so things don't hang when there is no response + * from the trf7970a (or tag). Using this mechanism creates a race with + * interrupts, however. That is, an interrupt and a timeout could occur + * closely enough together that one is blocked by the mutex while the other + * executes. When the timeout handler executes first and blocks the + * interrupt handler, it will eventually set the state to IDLE so the + * interrupt handler will check the state and exit with no harm done. + * When the interrupt handler executes first and blocks the timeout handler, + * the cancel_delayed_work() call will know that it didn't cancel the + * work item (i.e., timeout) and will return zero. That return code is + * used by the timer handler to indicate that it should ignore the timeout + * once its unblocked. + * + * Aborting an active command isn't as simple as it seems because the only + * way to abort a command that's already been sent to the tag is so turn + * off power to the tag. If we do that, though, we'd have to go through + * the entire anticollision procedure again but the digital layer doesn't + * support that. So, if an abort is received before trf7970a_in_send_cmd() + * has sent the command to the tag, it simply returns -ECANCELED. If the + * command has already been sent to the tag, then the driver continues + * normally and recieves the response data (or error) but just before + * sending the data upstream, it frees the rx_skb and sends -ECANCELED + * upstream instead. If the command failed, that error will be sent + * upstream. + * + * When recieving data from a tag and the interrupt status register has + * only the SRX bit set, it means that all of the data has been received + * (once what's in the fifo has been read). However, depending on timing + * an interrupt status with only the SRX bit set may not be recived. In + * those cases, the timeout mechanism is used to wait 5 ms in case more + * data arrives. After 5 ms, it is assumed that all of the data has been + * received and the accumulated rx data is sent upstream. The + * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose + * (i.e., it indicates that some data has been received but we're not sure + * if there is more coming so a timeout in this state means all data has + * been received and there isn't an error). The delay is 5 ms since delays + * over 2 ms have been observed during testing (a little extra just in case). + * + * Type 2 write and sector select commands respond with a 4-bit ACK or NACK. + * Having only 4 bits in the FIFO won't normally generate an interrupt so + * driver enables the '4_bit_RX' bit of the Special Functions register 1 + * to cause an interrupt in that case. Leaving that bit for a read command + * messes up the data returned so it is only enabled when the framing is + * 'NFC_DIGITAL_FRAMING_NFCA_T2T' and the command is not a read command. + * Unfortunately, that means that the driver has to peek into tx frames + * when the framing is 'NFC_DIGITAL_FRAMING_NFCA_T2T'. This is done by + * the trf7970a_per_cmd_config() routine. + * + * ISO/IEC 15693 frames specify whether to use single or double sub-carrier + * frequencies and whether to use low or high data rates in the flags byte + * of the frame. This means that the driver has to peek at all 15693 frames + * to determine what speed to set the communication to. In addition, write + * and lock commands use the OPTION flag to indicate that an EOF must be + * sent to the tag before it will send its response. So the driver has to + * examine all frames for that reason too. + * + * It is unclear how long to wait before sending the EOF. According to the + * Note under Table 1-1 in section 1.6 of + * http://www.ti.com/lit/ug/scbu011/scbu011.pdf, that wait should be at least + * 10 ms for TI Tag-it HF-I tags; however testing has shown that is not long + * enough. For this reason, the driver waits 20 ms which seems to work + * reliably. + */ + +#define TRF7970A_SUPPORTED_PROTOCOLS \ + (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO15693_MASK) + +/* TX data must be prefixed with a FIFO reset cmd, a cmd that depends + * on what the current framing is, the address of the TX length byte 1 + * register (0x1d), and the 2 byte length of the data to be transmitted. + * That totals 5 bytes. + */ +#define TRF7970A_TX_SKB_HEADROOM 5 + +#define TRF7970A_RX_SKB_ALLOC_SIZE 256 + +#define TRF7970A_FIFO_SIZE 128 + +/* TX length is 3 nibbles long ==> 4KB - 1 bytes max */ +#define TRF7970A_TX_MAX (4096 - 1) + +#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT 5 +#define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT 3 +#define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF 20 + +/* Quirks */ +/* Erratum: When reading IRQ Status register on trf7970a, we must issue a + * read continuous command for IRQ Status and Collision Position registers. + */ +#define TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA BIT(0) + +/* Direct commands */ +#define TRF7970A_CMD_IDLE 0x00 +#define TRF7970A_CMD_SOFT_INIT 0x03 +#define TRF7970A_CMD_RF_COLLISION 0x04 +#define TRF7970A_CMD_RF_COLLISION_RESPONSE_N 0x05 +#define TRF7970A_CMD_RF_COLLISION_RESPONSE_0 0x06 +#define TRF7970A_CMD_FIFO_RESET 0x0f +#define TRF7970A_CMD_TRANSMIT_NO_CRC 0x10 +#define TRF7970A_CMD_TRANSMIT 0x11 +#define TRF7970A_CMD_DELAY_TRANSMIT_NO_CRC 0x12 +#define TRF7970A_CMD_DELAY_TRANSMIT 0x13 +#define TRF7970A_CMD_EOF 0x14 +#define TRF7970A_CMD_CLOSE_SLOT 0x15 +#define TRF7970A_CMD_BLOCK_RX 0x16 +#define TRF7970A_CMD_ENABLE_RX 0x17 +#define TRF7970A_CMD_TEST_EXT_RF 0x18 +#define TRF7970A_CMD_TEST_INT_RF 0x19 +#define TRF7970A_CMD_RX_GAIN_ADJUST 0x1a + +/* Bits determining whether its a direct command or register R/W, + * whether to use a continuous SPI transaction or not, and the actual + * direct cmd opcode or regster address. + */ +#define TRF7970A_CMD_BIT_CTRL BIT(7) +#define TRF7970A_CMD_BIT_RW BIT(6) +#define TRF7970A_CMD_BIT_CONTINUOUS BIT(5) +#define TRF7970A_CMD_BIT_OPCODE(opcode) ((opcode) & 0x1f) + +/* Registers addresses */ +#define TRF7970A_CHIP_STATUS_CTRL 0x00 +#define TRF7970A_ISO_CTRL 0x01 +#define TRF7970A_ISO14443B_TX_OPTIONS 0x02 +#define TRF7970A_ISO14443A_HIGH_BITRATE_OPTIONS 0x03 +#define TRF7970A_TX_TIMER_SETTING_H_BYTE 0x04 +#define TRF7970A_TX_TIMER_SETTING_L_BYTE 0x05 +#define TRF7970A_TX_PULSE_LENGTH_CTRL 0x06 +#define TRF7970A_RX_NO_RESPONSE_WAIT 0x07 +#define TRF7970A_RX_WAIT_TIME 0x08 +#define TRF7970A_MODULATOR_SYS_CLK_CTRL 0x09 +#define TRF7970A_RX_SPECIAL_SETTINGS 0x0a +#define TRF7970A_REG_IO_CTRL 0x0b +#define TRF7970A_IRQ_STATUS 0x0c +#define TRF7970A_COLLISION_IRQ_MASK 0x0d +#define TRF7970A_COLLISION_POSITION 0x0e +#define TRF7970A_RSSI_OSC_STATUS 0x0f +#define TRF7970A_SPECIAL_FCN_REG1 0x10 +#define TRF7970A_SPECIAL_FCN_REG2 0x11 +#define TRF7970A_RAM1 0x12 +#define TRF7970A_RAM2 0x13 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS 0x14 +#define TRF7970A_NFC_LOW_FIELD_LEVEL 0x16 +#define TRF7970A_NFCID1 0x17 +#define TRF7970A_NFC_TARGET_LEVEL 0x18 +#define TRF79070A_NFC_TARGET_PROTOCOL 0x19 +#define TRF7970A_TEST_REGISTER1 0x1a +#define TRF7970A_TEST_REGISTER2 0x1b +#define TRF7970A_FIFO_STATUS 0x1c +#define TRF7970A_TX_LENGTH_BYTE1 0x1d +#define TRF7970A_TX_LENGTH_BYTE2 0x1e +#define TRF7970A_FIFO_IO_REGISTER 0x1f + +/* Chip Status Control Register Bits */ +#define TRF7970A_CHIP_STATUS_VRS5_3 BIT(0) +#define TRF7970A_CHIP_STATUS_REC_ON BIT(1) +#define TRF7970A_CHIP_STATUS_AGC_ON BIT(2) +#define TRF7970A_CHIP_STATUS_PM_ON BIT(3) +#define TRF7970A_CHIP_STATUS_RF_PWR BIT(4) +#define TRF7970A_CHIP_STATUS_RF_ON BIT(5) +#define TRF7970A_CHIP_STATUS_DIRECT BIT(6) +#define TRF7970A_CHIP_STATUS_STBY BIT(7) + +/* ISO Control Register Bits */ +#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_662 0x00 +#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_662 0x01 +#define TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648 0x02 +#define TRF7970A_ISO_CTRL_15693_SGL_1OF256_2648 0x03 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a 0x04 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_667 0x05 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669 0x06 +#define TRF7970A_ISO_CTRL_15693_DBL_1OF256_2669 0x07 +#define TRF7970A_ISO_CTRL_14443A_106 0x08 +#define TRF7970A_ISO_CTRL_14443A_212 0x09 +#define TRF7970A_ISO_CTRL_14443A_424 0x0a +#define TRF7970A_ISO_CTRL_14443A_848 0x0b +#define TRF7970A_ISO_CTRL_14443B_106 0x0c +#define TRF7970A_ISO_CTRL_14443B_212 0x0d +#define TRF7970A_ISO_CTRL_14443B_424 0x0e +#define TRF7970A_ISO_CTRL_14443B_848 0x0f +#define TRF7970A_ISO_CTRL_FELICA_212 0x1a +#define TRF7970A_ISO_CTRL_FELICA_424 0x1b +#define TRF7970A_ISO_CTRL_RFID BIT(5) +#define TRF7970A_ISO_CTRL_DIR_MODE BIT(6) +#define TRF7970A_ISO_CTRL_RX_CRC_N BIT(7) /* true == No CRC */ + +#define TRF7970A_ISO_CTRL_RFID_SPEED_MASK 0x1f + +/* Modulator and SYS_CLK Control Register Bits */ +#define TRF7970A_MODULATOR_DEPTH(n) ((n) & 0x7) +#define TRF7970A_MODULATOR_DEPTH_ASK10 (TRF7970A_MODULATOR_DEPTH(0)) +#define TRF7970A_MODULATOR_DEPTH_OOK (TRF7970A_MODULATOR_DEPTH(1)) +#define TRF7970A_MODULATOR_DEPTH_ASK7 (TRF7970A_MODULATOR_DEPTH(2)) +#define TRF7970A_MODULATOR_DEPTH_ASK8_5 (TRF7970A_MODULATOR_DEPTH(3)) +#define TRF7970A_MODULATOR_DEPTH_ASK13 (TRF7970A_MODULATOR_DEPTH(4)) +#define TRF7970A_MODULATOR_DEPTH_ASK16 (TRF7970A_MODULATOR_DEPTH(5)) +#define TRF7970A_MODULATOR_DEPTH_ASK22 (TRF7970A_MODULATOR_DEPTH(6)) +#define TRF7970A_MODULATOR_DEPTH_ASK30 (TRF7970A_MODULATOR_DEPTH(7)) +#define TRF7970A_MODULATOR_EN_ANA BIT(3) +#define TRF7970A_MODULATOR_CLK(n) (((n) & 0x3) << 4) +#define TRF7970A_MODULATOR_CLK_DISABLED (TRF7970A_MODULATOR_CLK(0)) +#define TRF7970A_MODULATOR_CLK_3_6 (TRF7970A_MODULATOR_CLK(1)) +#define TRF7970A_MODULATOR_CLK_6_13 (TRF7970A_MODULATOR_CLK(2)) +#define TRF7970A_MODULATOR_CLK_13_27 (TRF7970A_MODULATOR_CLK(3)) +#define TRF7970A_MODULATOR_EN_OOK BIT(6) +#define TRF7970A_MODULATOR_27MHZ BIT(7) + +/* IRQ Status Register Bits */ +#define TRF7970A_IRQ_STATUS_NORESP BIT(0) /* ISO15693 only */ +#define TRF7970A_IRQ_STATUS_COL BIT(1) +#define TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR BIT(2) +#define TRF7970A_IRQ_STATUS_PARITY_ERROR BIT(3) +#define TRF7970A_IRQ_STATUS_CRC_ERROR BIT(4) +#define TRF7970A_IRQ_STATUS_FIFO BIT(5) +#define TRF7970A_IRQ_STATUS_SRX BIT(6) +#define TRF7970A_IRQ_STATUS_TX BIT(7) + +#define TRF7970A_IRQ_STATUS_ERROR \ + (TRF7970A_IRQ_STATUS_COL | \ + TRF7970A_IRQ_STATUS_FRAMING_EOF_ERROR | \ + TRF7970A_IRQ_STATUS_PARITY_ERROR | \ + TRF7970A_IRQ_STATUS_CRC_ERROR) + +#define TRF7970A_SPECIAL_FCN_REG1_COL_7_6 BIT(0) +#define TRF7970A_SPECIAL_FCN_REG1_14_ANTICOLL BIT(1) +#define TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX BIT(2) +#define TRF7970A_SPECIAL_FCN_REG1_SP_DIR_MODE BIT(3) +#define TRF7970A_SPECIAL_FCN_REG1_NEXT_SLOT_37US BIT(4) +#define TRF7970A_SPECIAL_FCN_REG1_PAR43 BIT(5) + +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_124 (0x0 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_120 (0x1 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_112 (0x2 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 (0x3 << 2) +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_4 0x0 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_8 0x1 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_16 0x2 +#define TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32 0x3 + +#define TRF7970A_FIFO_STATUS_OVERFLOW BIT(7) + +/* NFC (ISO/IEC 14443A) Type 2 Tag commands */ +#define NFC_T2T_CMD_READ 0x30 + +/* ISO 15693 commands codes */ +#define ISO15693_CMD_INVENTORY 0x01 +#define ISO15693_CMD_READ_SINGLE_BLOCK 0x20 +#define ISO15693_CMD_WRITE_SINGLE_BLOCK 0x21 +#define ISO15693_CMD_LOCK_BLOCK 0x22 +#define ISO15693_CMD_READ_MULTIPLE_BLOCK 0x23 +#define ISO15693_CMD_WRITE_MULTIPLE_BLOCK 0x24 +#define ISO15693_CMD_SELECT 0x25 +#define ISO15693_CMD_RESET_TO_READY 0x26 +#define ISO15693_CMD_WRITE_AFI 0x27 +#define ISO15693_CMD_LOCK_AFI 0x28 +#define ISO15693_CMD_WRITE_DSFID 0x29 +#define ISO15693_CMD_LOCK_DSFID 0x2a +#define ISO15693_CMD_GET_SYSTEM_INFO 0x2b +#define ISO15693_CMD_GET_MULTIPLE_BLOCK_SECURITY_STATUS 0x2c + +/* ISO 15693 request and response flags */ +#define ISO15693_REQ_FLAG_SUB_CARRIER BIT(0) +#define ISO15693_REQ_FLAG_DATA_RATE BIT(1) +#define ISO15693_REQ_FLAG_INVENTORY BIT(2) +#define ISO15693_REQ_FLAG_PROTOCOL_EXT BIT(3) +#define ISO15693_REQ_FLAG_SELECT BIT(4) +#define ISO15693_REQ_FLAG_AFI BIT(4) +#define ISO15693_REQ_FLAG_ADDRESS BIT(5) +#define ISO15693_REQ_FLAG_NB_SLOTS BIT(5) +#define ISO15693_REQ_FLAG_OPTION BIT(6) + +#define ISO15693_REQ_FLAG_SPEED_MASK \ + (ISO15693_REQ_FLAG_SUB_CARRIER | ISO15693_REQ_FLAG_DATA_RATE) + +enum trf7970a_state { + TRF7970A_ST_OFF, + TRF7970A_ST_IDLE, + TRF7970A_ST_IDLE_RX_BLOCKED, + TRF7970A_ST_WAIT_FOR_TX_FIFO, + TRF7970A_ST_WAIT_FOR_RX_DATA, + TRF7970A_ST_WAIT_FOR_RX_DATA_CONT, + TRF7970A_ST_WAIT_TO_ISSUE_EOF, + TRF7970A_ST_MAX +}; + +struct trf7970a { + enum trf7970a_state state; + struct device *dev; + struct spi_device *spi; + struct regulator *regulator; + struct nfc_digital_dev *ddev; + u32 quirks; + bool powering_up; + bool aborting; + struct sk_buff *tx_skb; + struct sk_buff *rx_skb; + nfc_digital_cmd_complete_t cb; + void *cb_arg; + u8 iso_ctrl; + u8 special_fcn_reg1; + int technology; + int framing; + u8 tx_cmd; + bool issue_eof; + int en2_gpio; + int en_gpio; + struct mutex lock; + unsigned int timeout; + bool ignore_timeout; + struct delayed_work timeout_work; +}; + + +static int trf7970a_cmd(struct trf7970a *trf, u8 opcode) +{ + u8 cmd = TRF7970A_CMD_BIT_CTRL | TRF7970A_CMD_BIT_OPCODE(opcode); + int ret; + + dev_dbg(trf->dev, "cmd: 0x%x\n", cmd); + + ret = spi_write(trf->spi, &cmd, 1); + if (ret) + dev_err(trf->dev, "%s - cmd: 0x%x, ret: %d\n", __func__, cmd, + ret); + return ret; +} + +static int trf7970a_read(struct trf7970a *trf, u8 reg, u8 *val) +{ + u8 addr = TRF7970A_CMD_BIT_RW | reg; + int ret; + + ret = spi_write_then_read(trf->spi, &addr, 1, val, 1); + if (ret) + dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr, + ret); + + dev_dbg(trf->dev, "read(0x%x): 0x%x\n", addr, *val); + + return ret; +} + +static int trf7970a_read_cont(struct trf7970a *trf, u8 reg, + u8 *buf, size_t len) +{ + u8 addr = reg | TRF7970A_CMD_BIT_RW | TRF7970A_CMD_BIT_CONTINUOUS; + int ret; + + dev_dbg(trf->dev, "read_cont(0x%x, %zd)\n", addr, len); + + ret = spi_write_then_read(trf->spi, &addr, 1, buf, len); + if (ret) + dev_err(trf->dev, "%s - addr: 0x%x, ret: %d\n", __func__, addr, + ret); + return ret; +} + +static int trf7970a_write(struct trf7970a *trf, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + int ret; + + dev_dbg(trf->dev, "write(0x%x): 0x%x\n", reg, val); + + ret = spi_write(trf->spi, buf, 2); + if (ret) + dev_err(trf->dev, "%s - write: 0x%x 0x%x, ret: %d\n", __func__, + buf[0], buf[1], ret); + + return ret; +} + +static int trf7970a_read_irqstatus(struct trf7970a *trf, u8 *status) +{ + int ret; + u8 buf[2]; + u8 addr; + + addr = TRF7970A_IRQ_STATUS | TRF7970A_CMD_BIT_RW; + + if (trf->quirks & TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA) { + addr |= TRF7970A_CMD_BIT_CONTINUOUS; + ret = spi_write_then_read(trf->spi, &addr, 1, buf, 2); + } else { + ret = spi_write_then_read(trf->spi, &addr, 1, buf, 1); + } + + if (ret) + dev_err(trf->dev, "%s - irqstatus: Status read failed: %d\n", + __func__, ret); + else + *status = buf[0]; + + return ret; +} + +static void trf7970a_send_upstream(struct trf7970a *trf) +{ + u8 rssi; + + dev_kfree_skb_any(trf->tx_skb); + trf->tx_skb = NULL; + + if (trf->rx_skb && !IS_ERR(trf->rx_skb) && !trf->aborting) + print_hex_dump_debug("trf7970a rx data: ", DUMP_PREFIX_NONE, + 16, 1, trf->rx_skb->data, trf->rx_skb->len, + false); + + /* According to the manual it is "good form" to reset the fifo and + * read the RSSI levels & oscillator status register here. It doesn't + * explain why. + */ + trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + trf7970a_read(trf, TRF7970A_RSSI_OSC_STATUS, &rssi); + + trf->state = TRF7970A_ST_IDLE; + + if (trf->aborting) { + dev_dbg(trf->dev, "Abort process complete\n"); + + if (!IS_ERR(trf->rx_skb)) { + kfree_skb(trf->rx_skb); + trf->rx_skb = ERR_PTR(-ECANCELED); + } + + trf->aborting = false; + } + + trf->cb(trf->ddev, trf->cb_arg, trf->rx_skb); + + trf->rx_skb = NULL; +} + +static void trf7970a_send_err_upstream(struct trf7970a *trf, int errno) +{ + dev_dbg(trf->dev, "Error - state: %d, errno: %d\n", trf->state, errno); + + kfree_skb(trf->rx_skb); + trf->rx_skb = ERR_PTR(errno); + + trf7970a_send_upstream(trf); +} + +static int trf7970a_transmit(struct trf7970a *trf, struct sk_buff *skb, + unsigned int len) +{ + unsigned int timeout; + int ret; + + print_hex_dump_debug("trf7970a tx data: ", DUMP_PREFIX_NONE, + 16, 1, skb->data, len, false); + + ret = spi_write(trf->spi, skb->data, len); + if (ret) { + dev_err(trf->dev, "%s - Can't send tx data: %d\n", __func__, + ret); + return ret; + } + + skb_pull(skb, len); + + if (skb->len > 0) { + trf->state = TRF7970A_ST_WAIT_FOR_TX_FIFO; + timeout = TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT; + } else { + if (trf->issue_eof) { + trf->state = TRF7970A_ST_WAIT_TO_ISSUE_EOF; + timeout = TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF; + } else { + trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA; + timeout = trf->timeout; + } + } + + dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n", timeout, + trf->state); + + schedule_delayed_work(&trf->timeout_work, msecs_to_jiffies(timeout)); + + return 0; +} + +static void trf7970a_fill_fifo(struct trf7970a *trf) +{ + struct sk_buff *skb = trf->tx_skb; + unsigned int len; + int ret; + u8 fifo_bytes; + + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + dev_dbg(trf->dev, "Filling FIFO - fifo_bytes: 0x%x\n", fifo_bytes); + + if (fifo_bytes & TRF7970A_FIFO_STATUS_OVERFLOW) { + dev_err(trf->dev, "%s - fifo overflow: 0x%x\n", __func__, + fifo_bytes); + trf7970a_send_err_upstream(trf, -EIO); + return; + } + + /* Calculate how much more data can be written to the fifo */ + len = TRF7970A_FIFO_SIZE - fifo_bytes; + len = min(skb->len, len); + + ret = trf7970a_transmit(trf, skb, len); + if (ret) + trf7970a_send_err_upstream(trf, ret); +} + +static void trf7970a_drain_fifo(struct trf7970a *trf, u8 status) +{ + struct sk_buff *skb = trf->rx_skb; + int ret; + u8 fifo_bytes; + + if (status & TRF7970A_IRQ_STATUS_ERROR) { + trf7970a_send_err_upstream(trf, -EIO); + return; + } + + ret = trf7970a_read(trf, TRF7970A_FIFO_STATUS, &fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + dev_dbg(trf->dev, "Draining FIFO - fifo_bytes: 0x%x\n", fifo_bytes); + + if (!fifo_bytes) + goto no_rx_data; + + if (fifo_bytes & TRF7970A_FIFO_STATUS_OVERFLOW) { + dev_err(trf->dev, "%s - fifo overflow: 0x%x\n", __func__, + fifo_bytes); + trf7970a_send_err_upstream(trf, -EIO); + return; + } + + if (fifo_bytes > skb_tailroom(skb)) { + skb = skb_copy_expand(skb, skb_headroom(skb), + max_t(int, fifo_bytes, + TRF7970A_RX_SKB_ALLOC_SIZE), + GFP_KERNEL); + if (!skb) { + trf7970a_send_err_upstream(trf, -ENOMEM); + return; + } + + kfree_skb(trf->rx_skb); + trf->rx_skb = skb; + } + + ret = trf7970a_read_cont(trf, TRF7970A_FIFO_IO_REGISTER, + skb_put(skb, fifo_bytes), fifo_bytes); + if (ret) { + trf7970a_send_err_upstream(trf, ret); + return; + } + + /* If received Type 2 ACK/NACK, shift right 4 bits and pass up */ + if ((trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T) && (skb->len == 1) && + (trf->special_fcn_reg1 == + TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX)) { + skb->data[0] >>= 4; + status = TRF7970A_IRQ_STATUS_SRX; + } else { + trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA_CONT; + } + +no_rx_data: + if (status == TRF7970A_IRQ_STATUS_SRX) { /* Receive complete */ + trf7970a_send_upstream(trf); + return; + } + + dev_dbg(trf->dev, "Setting timeout for %d ms\n", + TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT); + + schedule_delayed_work(&trf->timeout_work, + msecs_to_jiffies(TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT)); +} + +static irqreturn_t trf7970a_irq(int irq, void *dev_id) +{ + struct trf7970a *trf = dev_id; + int ret; + u8 status; + + mutex_lock(&trf->lock); + + if (trf->state == TRF7970A_ST_OFF) { + mutex_unlock(&trf->lock); + return IRQ_NONE; + } + + ret = trf7970a_read_irqstatus(trf, &status); + if (ret) { + mutex_unlock(&trf->lock); + return IRQ_NONE; + } + + dev_dbg(trf->dev, "IRQ - state: %d, status: 0x%x\n", trf->state, + status); + + if (!status) { + mutex_unlock(&trf->lock); + return IRQ_NONE; + } + + switch (trf->state) { + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + /* If getting interrupts caused by RF noise, turn off the + * receiver to avoid unnecessary interrupts. It will be + * turned back on in trf7970a_in_send_cmd() when the next + * command is issued. + */ + if (status & TRF7970A_IRQ_STATUS_ERROR) { + trf7970a_cmd(trf, TRF7970A_CMD_BLOCK_RX); + trf->state = TRF7970A_ST_IDLE_RX_BLOCKED; + } + + trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + break; + case TRF7970A_ST_WAIT_FOR_TX_FIFO: + if (status & TRF7970A_IRQ_STATUS_TX) { + trf->ignore_timeout = + !cancel_delayed_work(&trf->timeout_work); + trf7970a_fill_fifo(trf); + } else { + trf7970a_send_err_upstream(trf, -EIO); + } + break; + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + if (status & TRF7970A_IRQ_STATUS_SRX) { + trf->ignore_timeout = + !cancel_delayed_work(&trf->timeout_work); + trf7970a_drain_fifo(trf, status); + } else if (!(status & TRF7970A_IRQ_STATUS_TX)) { + trf7970a_send_err_upstream(trf, -EIO); + } + break; + case TRF7970A_ST_WAIT_TO_ISSUE_EOF: + if (status != TRF7970A_IRQ_STATUS_TX) + trf7970a_send_err_upstream(trf, -EIO); + break; + default: + dev_err(trf->dev, "%s - Driver in invalid state: %d\n", + __func__, trf->state); + } + + mutex_unlock(&trf->lock); + return IRQ_HANDLED; +} + +static void trf7970a_issue_eof(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Issuing EOF\n"); + + ret = trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET); + if (ret) + trf7970a_send_err_upstream(trf, ret); + + ret = trf7970a_cmd(trf, TRF7970A_CMD_EOF); + if (ret) + trf7970a_send_err_upstream(trf, ret); + + trf->state = TRF7970A_ST_WAIT_FOR_RX_DATA; + + dev_dbg(trf->dev, "Setting timeout for %d ms, state: %d\n", + trf->timeout, trf->state); + + schedule_delayed_work(&trf->timeout_work, + msecs_to_jiffies(trf->timeout)); +} + +static void trf7970a_timeout_work_handler(struct work_struct *work) +{ + struct trf7970a *trf = container_of(work, struct trf7970a, + timeout_work.work); + + dev_dbg(trf->dev, "Timeout - state: %d, ignore_timeout: %d\n", + trf->state, trf->ignore_timeout); + + mutex_lock(&trf->lock); + + if (trf->ignore_timeout) + trf->ignore_timeout = false; + else if (trf->state == TRF7970A_ST_WAIT_FOR_RX_DATA_CONT) + trf7970a_send_upstream(trf); /* No more rx data so send up */ + else if (trf->state == TRF7970A_ST_WAIT_TO_ISSUE_EOF) + trf7970a_issue_eof(trf); + else + trf7970a_send_err_upstream(trf, -ETIMEDOUT); + + mutex_unlock(&trf->lock); +} + +static int trf7970a_init(struct trf7970a *trf) +{ + int ret; + + dev_dbg(trf->dev, "Initializing device - state: %d\n", trf->state); + + ret = trf7970a_cmd(trf, TRF7970A_CMD_SOFT_INIT); + if (ret) + goto err_out; + + ret = trf7970a_cmd(trf, TRF7970A_CMD_IDLE); + if (ret) + goto err_out; + + ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, + TRF7970A_MODULATOR_DEPTH_OOK); + if (ret) + goto err_out; + + ret = trf7970a_write(trf, TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS, + TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 | + TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32); + if (ret) + goto err_out; + + ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1, 0); + if (ret) + goto err_out; + + trf->special_fcn_reg1 = 0; + + ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, + TRF7970A_CHIP_STATUS_RF_ON | + TRF7970A_CHIP_STATUS_VRS5_3); + if (ret) + goto err_out; + + return 0; + +err_out: + dev_dbg(trf->dev, "Couldn't init device: %d\n", ret); + return ret; +} + +static void trf7970a_switch_rf_off(struct trf7970a *trf) +{ + dev_dbg(trf->dev, "Switching rf off\n"); + + gpio_set_value(trf->en_gpio, 0); + gpio_set_value(trf->en2_gpio, 0); + + trf->aborting = false; + trf->state = TRF7970A_ST_OFF; +} + +static int trf7970a_switch_rf_on(struct trf7970a *trf) +{ + unsigned long delay; + int ret; + + dev_dbg(trf->dev, "Switching rf on\n"); + + if (trf->powering_up) + usleep_range(5000, 6000); + + gpio_set_value(trf->en2_gpio, 1); + usleep_range(1000, 2000); + gpio_set_value(trf->en_gpio, 1); + + /* The delay between enabling the trf7970a and issuing the first + * command is significantly longer the very first time after powering + * up. Make sure the longer delay is only done the first time. + */ + if (trf->powering_up) { + delay = 20000; + trf->powering_up = false; + } else { + delay = 5000; + } + + usleep_range(delay, delay + 1000); + + ret = trf7970a_init(trf); + if (ret) + trf7970a_switch_rf_off(trf); + else + trf->state = TRF7970A_ST_IDLE; + + return ret; +} + +static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret = 0; + + dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on); + + mutex_lock(&trf->lock); + + if (on) { + switch (trf->state) { + case TRF7970A_ST_OFF: + ret = trf7970a_switch_rf_on(trf); + break; + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + break; + default: + dev_err(trf->dev, "%s - Invalid request: %d %d\n", + __func__, trf->state, on); + trf7970a_switch_rf_off(trf); + } + } else { + switch (trf->state) { + case TRF7970A_ST_OFF: + break; + default: + dev_err(trf->dev, "%s - Invalid request: %d %d\n", + __func__, trf->state, on); + /* FALLTHROUGH */ + case TRF7970A_ST_IDLE: + case TRF7970A_ST_IDLE_RX_BLOCKED: + trf7970a_switch_rf_off(trf); + } + } + + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech) +{ + int ret = 0; + + dev_dbg(trf->dev, "rf technology: %d\n", tech); + + switch (tech) { + case NFC_DIGITAL_RF_TECH_106A: + trf->iso_ctrl = TRF7970A_ISO_CTRL_14443A_106; + break; + case NFC_DIGITAL_RF_TECH_ISO15693: + trf->iso_ctrl = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; + break; + default: + dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech); + return -EINVAL; + } + + trf->technology = tech; + + return ret; +} + +static int trf7970a_config_framing(struct trf7970a *trf, int framing) +{ + dev_dbg(trf->dev, "framing: %d\n", framing); + + switch (framing) { + case NFC_DIGITAL_FRAMING_NFCA_SHORT: + case NFC_DIGITAL_FRAMING_NFCA_STANDARD: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC; + trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + break; + case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: + case NFC_DIGITAL_FRAMING_NFCA_T4T: + case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: + case NFC_DIGITAL_FRAMING_ISO15693_T5T: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + trf->iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N; + break; + case NFC_DIGITAL_FRAMING_NFCA_T2T: + trf->tx_cmd = TRF7970A_CMD_TRANSMIT; + trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N; + break; + default: + dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing); + return -EINVAL; + } + + trf->framing = framing; + + return trf7970a_write(trf, TRF7970A_ISO_CTRL, trf->iso_ctrl); +} + +static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + int ret = 0; + + dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param); + + mutex_lock(&trf->lock); + + if (trf->state == TRF7970A_ST_OFF) { + ret = trf7970a_switch_rf_on(trf); + if (ret) + goto err_out; + } + + switch (type) { + case NFC_DIGITAL_CONFIG_RF_TECH: + ret = trf7970a_config_rf_tech(trf, param); + break; + case NFC_DIGITAL_CONFIG_FRAMING: + ret = trf7970a_config_framing(trf, param); + break; + default: + dev_dbg(trf->dev, "Unknown type: %d\n", type); + ret = -EINVAL; + } + +err_out: + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_is_iso15693_write_or_lock(u8 cmd) +{ + switch (cmd) { + case ISO15693_CMD_WRITE_SINGLE_BLOCK: + case ISO15693_CMD_LOCK_BLOCK: + case ISO15693_CMD_WRITE_MULTIPLE_BLOCK: + case ISO15693_CMD_WRITE_AFI: + case ISO15693_CMD_LOCK_AFI: + case ISO15693_CMD_WRITE_DSFID: + case ISO15693_CMD_LOCK_DSFID: + return 1; + break; + default: + return 0; + } +} + +static int trf7970a_per_cmd_config(struct trf7970a *trf, struct sk_buff *skb) +{ + u8 *req = skb->data; + u8 special_fcn_reg1, iso_ctrl; + int ret; + + trf->issue_eof = false; + + /* When issuing Type 2 read command, make sure the '4_bit_RX' bit in + * special functions register 1 is cleared; otherwise, its a write or + * sector select command and '4_bit_RX' must be set. + * + * When issuing an ISO 15693 command, inspect the flags byte to see + * what speed to use. Also, remember if the OPTION flag is set on + * a Type 5 write or lock command so the driver will know that it + * has to send an EOF in order to get a response. + */ + if ((trf->technology == NFC_DIGITAL_RF_TECH_106A) && + (trf->framing == NFC_DIGITAL_FRAMING_NFCA_T2T)) { + if (req[0] == NFC_T2T_CMD_READ) + special_fcn_reg1 = 0; + else + special_fcn_reg1 = TRF7970A_SPECIAL_FCN_REG1_4_BIT_RX; + + if (special_fcn_reg1 != trf->special_fcn_reg1) { + ret = trf7970a_write(trf, TRF7970A_SPECIAL_FCN_REG1, + special_fcn_reg1); + if (ret) + return ret; + + trf->special_fcn_reg1 = special_fcn_reg1; + } + } else if (trf->technology == NFC_DIGITAL_RF_TECH_ISO15693) { + iso_ctrl = trf->iso_ctrl & ~TRF7970A_ISO_CTRL_RFID_SPEED_MASK; + + switch (req[0] & ISO15693_REQ_FLAG_SPEED_MASK) { + case 0x00: + iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_662; + break; + case ISO15693_REQ_FLAG_SUB_CARRIER: + iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_667a; + break; + case ISO15693_REQ_FLAG_DATA_RATE: + iso_ctrl |= TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; + break; + case (ISO15693_REQ_FLAG_SUB_CARRIER | + ISO15693_REQ_FLAG_DATA_RATE): + iso_ctrl |= TRF7970A_ISO_CTRL_15693_DBL_1OF4_2669; + break; + } + + if (iso_ctrl != trf->iso_ctrl) { + ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl); + if (ret) + return ret; + + trf->iso_ctrl = iso_ctrl; + } + + if ((trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) && + trf7970a_is_iso15693_write_or_lock(req[1]) && + (req[0] & ISO15693_REQ_FLAG_OPTION)) + trf->issue_eof = true; + } + + return 0; +} + +static int trf7970a_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + char *prefix; + unsigned int len; + int ret; + + dev_dbg(trf->dev, "New request - state: %d, timeout: %d ms, len: %d\n", + trf->state, timeout, skb->len); + + if (skb->len > TRF7970A_TX_MAX) + return -EINVAL; + + mutex_lock(&trf->lock); + + if ((trf->state != TRF7970A_ST_IDLE) && + (trf->state != TRF7970A_ST_IDLE_RX_BLOCKED)) { + dev_err(trf->dev, "%s - Bogus state: %d\n", __func__, + trf->state); + ret = -EIO; + goto out_err; + } + + if (trf->aborting) { + dev_dbg(trf->dev, "Abort process complete\n"); + trf->aborting = false; + ret = -ECANCELED; + goto out_err; + } + + trf->rx_skb = nfc_alloc_recv_skb(TRF7970A_RX_SKB_ALLOC_SIZE, + GFP_KERNEL); + if (!trf->rx_skb) { + dev_dbg(trf->dev, "Can't alloc rx_skb\n"); + ret = -ENOMEM; + goto out_err; + } + + if (trf->state == TRF7970A_ST_IDLE_RX_BLOCKED) { + ret = trf7970a_cmd(trf, TRF7970A_CMD_ENABLE_RX); + if (ret) + goto out_err; + + trf->state = TRF7970A_ST_IDLE; + } + + ret = trf7970a_per_cmd_config(trf, skb); + if (ret) + goto out_err; + + trf->ddev = ddev; + trf->tx_skb = skb; + trf->cb = cb; + trf->cb_arg = arg; + trf->timeout = timeout; + trf->ignore_timeout = false; + + len = skb->len; + prefix = skb_push(skb, TRF7970A_TX_SKB_HEADROOM); + + /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends + * on what the current framing is, the address of the TX length byte 1 + * register (0x1d), and the 2 byte length of the data to be transmitted. + */ + prefix[0] = TRF7970A_CMD_BIT_CTRL | + TRF7970A_CMD_BIT_OPCODE(TRF7970A_CMD_FIFO_RESET); + prefix[1] = TRF7970A_CMD_BIT_CTRL | + TRF7970A_CMD_BIT_OPCODE(trf->tx_cmd); + prefix[2] = TRF7970A_CMD_BIT_CONTINUOUS | TRF7970A_TX_LENGTH_BYTE1; + + if (trf->framing == NFC_DIGITAL_FRAMING_NFCA_SHORT) { + prefix[3] = 0x00; + prefix[4] = 0x0f; /* 7 bits */ + } else { + prefix[3] = (len & 0xf00) >> 4; + prefix[3] |= ((len & 0xf0) >> 4); + prefix[4] = ((len & 0x0f) << 4); + } + + len = min_t(int, skb->len, TRF7970A_FIFO_SIZE); + + usleep_range(1000, 2000); + + ret = trf7970a_transmit(trf, skb, len); + if (ret) { + kfree_skb(trf->rx_skb); + trf->rx_skb = NULL; + } + +out_err: + mutex_unlock(&trf->lock); + return ret; +} + +static int trf7970a_tg_configure_hw(struct nfc_digital_dev *ddev, + int type, int param) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Unsupported interface\n"); + + return -EINVAL; +} + +static int trf7970a_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Unsupported interface\n"); + + return -EINVAL; +} + +static int trf7970a_tg_listen(struct nfc_digital_dev *ddev, + u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Unsupported interface\n"); + + return -EINVAL; +} + +static int trf7970a_tg_listen_mdaa(struct nfc_digital_dev *ddev, + struct digital_tg_mdaa_params *mdaa_params, + u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Unsupported interface\n"); + + return -EINVAL; +} + +static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev) +{ + struct trf7970a *trf = nfc_digital_get_drvdata(ddev); + + dev_dbg(trf->dev, "Abort process initiated\n"); + + mutex_lock(&trf->lock); + trf->aborting = true; + mutex_unlock(&trf->lock); +} + +static struct nfc_digital_ops trf7970a_nfc_ops = { + .in_configure_hw = trf7970a_in_configure_hw, + .in_send_cmd = trf7970a_in_send_cmd, + .tg_configure_hw = trf7970a_tg_configure_hw, + .tg_send_cmd = trf7970a_tg_send_cmd, + .tg_listen = trf7970a_tg_listen, + .tg_listen_mdaa = trf7970a_tg_listen_mdaa, + .switch_rf = trf7970a_switch_rf, + .abort_cmd = trf7970a_abort_cmd, +}; + +static int trf7970a_probe(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + const struct spi_device_id *id = spi_get_device_id(spi); + struct trf7970a *trf; + int ret; + + if (!np) { + dev_err(&spi->dev, "No Device Tree entry\n"); + return -EINVAL; + } + + trf = devm_kzalloc(&spi->dev, sizeof(*trf), GFP_KERNEL); + if (!trf) + return -ENOMEM; + + trf->state = TRF7970A_ST_OFF; + trf->dev = &spi->dev; + trf->spi = spi; + trf->quirks = id->driver_data; + + spi->mode = SPI_MODE_1; + spi->bits_per_word = 8; + + /* There are two enable pins - both must be present */ + trf->en_gpio = of_get_named_gpio(np, "ti,enable-gpios", 0); + if (!gpio_is_valid(trf->en_gpio)) { + dev_err(trf->dev, "No EN GPIO property\n"); + return trf->en_gpio; + } + + ret = devm_gpio_request_one(trf->dev, trf->en_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "EN"); + if (ret) { + dev_err(trf->dev, "Can't request EN GPIO: %d\n", ret); + return ret; + } + + trf->en2_gpio = of_get_named_gpio(np, "ti,enable-gpios", 1); + if (!gpio_is_valid(trf->en2_gpio)) { + dev_err(trf->dev, "No EN2 GPIO property\n"); + return trf->en2_gpio; + } + + ret = devm_gpio_request_one(trf->dev, trf->en2_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "EN2"); + if (ret) { + dev_err(trf->dev, "Can't request EN2 GPIO: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(trf->dev, spi->irq, NULL, + trf7970a_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "trf7970a", trf); + if (ret) { + dev_err(trf->dev, "Can't request IRQ#%d: %d\n", spi->irq, ret); + return ret; + } + + mutex_init(&trf->lock); + INIT_DELAYED_WORK(&trf->timeout_work, trf7970a_timeout_work_handler); + + trf->regulator = devm_regulator_get(&spi->dev, "vin"); + if (IS_ERR(trf->regulator)) { + ret = PTR_ERR(trf->regulator); + dev_err(trf->dev, "Can't get VIN regulator: %d\n", ret); + goto err_destroy_lock; + } + + ret = regulator_enable(trf->regulator); + if (ret) { + dev_err(trf->dev, "Can't enable VIN: %d\n", ret); + goto err_destroy_lock; + } + + trf->powering_up = true; + + trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops, + TRF7970A_SUPPORTED_PROTOCOLS, + NFC_DIGITAL_DRV_CAPS_IN_CRC, TRF7970A_TX_SKB_HEADROOM, + 0); + if (!trf->ddev) { + dev_err(trf->dev, "Can't allocate NFC digital device\n"); + ret = -ENOMEM; + goto err_disable_regulator; + } + + nfc_digital_set_parent_dev(trf->ddev, trf->dev); + nfc_digital_set_drvdata(trf->ddev, trf); + spi_set_drvdata(spi, trf); + + ret = nfc_digital_register_device(trf->ddev); + if (ret) { + dev_err(trf->dev, "Can't register NFC digital device: %d\n", + ret); + goto err_free_ddev; + } + + return 0; + +err_free_ddev: + nfc_digital_free_device(trf->ddev); +err_disable_regulator: + regulator_disable(trf->regulator); +err_destroy_lock: + mutex_destroy(&trf->lock); + return ret; +} + +static int trf7970a_remove(struct spi_device *spi) +{ + struct trf7970a *trf = spi_get_drvdata(spi); + + mutex_lock(&trf->lock); + + trf7970a_switch_rf_off(trf); + trf7970a_init(trf); + + switch (trf->state) { + case TRF7970A_ST_WAIT_FOR_TX_FIFO: + case TRF7970A_ST_WAIT_FOR_RX_DATA: + case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT: + case TRF7970A_ST_WAIT_TO_ISSUE_EOF: + trf7970a_send_err_upstream(trf, -ECANCELED); + break; + default: + break; + } + + mutex_unlock(&trf->lock); + + nfc_digital_unregister_device(trf->ddev); + nfc_digital_free_device(trf->ddev); + + regulator_disable(trf->regulator); + + mutex_destroy(&trf->lock); + + return 0; +} + +static const struct spi_device_id trf7970a_id_table[] = { + { "trf7970a", TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA }, + { } +}; +MODULE_DEVICE_TABLE(spi, trf7970a_id_table); + +static struct spi_driver trf7970a_spi_driver = { + .probe = trf7970a_probe, + .remove = trf7970a_remove, + .id_table = trf7970a_id_table, + .driver = { + .name = "trf7970a", + .owner = THIS_MODULE, + }, +}; + +module_spi_driver(trf7970a_spi_driver); + +MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI trf7970a RFID/NFC Transceiver Driver"); |